Add plugin wizard initialization step (#2045)

Adds a new initialization step after setting up the initial administration account that allows administrators to initialize the instance with a selection of plugin sets.

Co-authored-by: René Pfeuffer <rene.pfeuffer@cloudogu.com>
Co-authored-by: Eduard Heimbuch <eduard.heimbuch@cloudogu.com>
Co-authored-by: Matthias Thieroff <matthias.thieroff@cloudogu.com>
This commit is contained in:
Konstantin Schaper
2022-05-31 15:14:52 +02:00
committed by Eduard Heimbuch
parent 6216945f0d
commit 1b18191c57
63 changed files with 2294 additions and 135 deletions

View File

@@ -24,6 +24,9 @@
package sonia.scm.api.v2.resources;
import com.github.sdorra.shiro.SubjectAware;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ThreadContext;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.jupiter.api.BeforeEach;
@@ -33,10 +36,19 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.initialization.InitializationAuthenticationService;
import sonia.scm.lifecycle.AdminAccountStartupAction;
import sonia.scm.security.AccessToken;
import sonia.scm.security.AccessTokenBuilder;
import sonia.scm.security.AccessTokenBuilderFactory;
import sonia.scm.security.AccessTokenCookieIssuer;
import sonia.scm.security.DefaultAccessTokenCookieIssuer;
import sonia.scm.web.RestDispatcher;
import javax.inject.Provider;
import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.net.URISyntaxException;
import static java.lang.String.format;
@@ -46,9 +58,13 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.jboss.resteasy.mock.MockHttpRequest.post;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SubjectAware(
configuration = "classpath:sonia/scm/repository/shiro.ini"
)
@ExtendWith(MockitoExtension.class)
class AdminAccountStartupResourceTest {
@@ -60,9 +76,16 @@ class AdminAccountStartupResourceTest {
@Mock
private Provider<ScmPathInfoStore> pathInfoStoreProvider;
@Mock
private InitializationAuthenticationService authenticationService;
@Mock
private ScmPathInfoStore pathInfoStore;
@Mock
private ScmPathInfo pathInfo;
@Mock
private AccessTokenBuilderFactory accessTokenBuilderFactory;
@Mock
private AccessTokenBuilder accessTokenBuilder;
private final AccessTokenCookieIssuer cookieIssuer = new DefaultAccessTokenCookieIssuer(mock(ScmConfiguration.class));
@InjectMocks
private AdminAccountStartupResource resource;
@@ -73,6 +96,9 @@ class AdminAccountStartupResourceTest {
lenient().when(pathInfoStore.get()).thenReturn(pathInfo);
dispatcher.addSingletonResource(new InitializationResource(singleton(resource)));
lenient().when(startupAction.name()).thenReturn("adminAccount");
lenient().when(accessTokenBuilderFactory.create()).thenReturn(accessTokenBuilder);
AccessToken accessToken = mock(AccessToken.class);
lenient().when(accessTokenBuilder.build()).thenReturn(accessToken);
}
@Test
@@ -121,10 +147,17 @@ class AdminAccountStartupResourceTest {
@Test
void shouldCreateAdminUser() throws URISyntaxException {
Subject subject = mock(Subject.class);
ThreadContext.bind(subject);
MockHttpRequest request =
post("/v2/initialization/adminAccount")
.contentType("application/json")
.content(createInput("initial-token", "trillian", "Tricia", "tricia@hitchhiker.com", "password", "password"));
HttpServletRequest servletRequest = mock(HttpServletRequest.class);
dispatcher.putDefaultContextObject(HttpServletRequest.class, servletRequest);
dispatcher.invoke(request, response);
assertThat(response.getStatus()).isEqualTo(204);

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.api.v2.resources;
import de.otto.edison.hal.HalRepresentation;

View File

@@ -183,7 +183,7 @@ class IndexDtoGeneratorTest {
Embedded.Builder initializationEmbeddedBuilder = invocationOnMock.getArgument(1, Embedded.Builder.class);
initializationLinkBuilder.single(link("init", "/init"));
return null;
}).when(initializationStepResource).setupIndex(any(), any());
}).when(initializationStepResource).setupIndex(any(), any(), any());
IndexDto dto = generator.generate();

View File

@@ -30,19 +30,24 @@ import org.assertj.core.api.Assertions;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.SCMContextProvider;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.initialization.InitializationFinisher;
import sonia.scm.plugin.PluginCenterAuthenticator;
import sonia.scm.search.SearchEngine;
import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.util.Locale;
import java.util.Optional;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@SubjectAware(configuration = "classpath:sonia/scm/shiro-002.ini")
@RunWith(MockitoJUnitRunner.class)
public class IndexResourceTest {
@Rule
@@ -52,8 +57,12 @@ public class IndexResourceTest {
private SCMContextProvider scmContextProvider;
private IndexResource indexResource;
@Mock
private HttpServletRequest httpServletRequest;
@Before
public void setUpObjectUnderTest() {
when(httpServletRequest.getLocale()).thenReturn(Locale.ENGLISH);
this.configuration = new ScmConfiguration();
this.scmContextProvider = mock(SCMContextProvider.class);
InitializationFinisher initializationFinisher = mock(InitializationFinisher.class);
@@ -72,7 +81,7 @@ public class IndexResourceTest {
@Test
@SubjectAware(username = "dent", password = "secret")
public void shouldRenderPluginCenterAuthLink() {
IndexDto index = indexResource.getIndex();
IndexDto index = indexResource.getIndex(httpServletRequest);
Assertions.assertThat(index.getLinks().getLinkBy("pluginCenterAuth")).isPresent();
}
@@ -80,21 +89,21 @@ public class IndexResourceTest {
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldNotRenderPluginCenterLoginLinkIfPermissionsAreMissing() {
IndexDto index = indexResource.getIndex();
IndexDto index = indexResource.getIndex(httpServletRequest);
Assertions.assertThat(index.getLinks().getLinkBy("pluginCenterAuth")).isNotPresent();
}
@Test
public void shouldRenderLoginUrlsForUnauthenticatedRequest() {
IndexDto index = indexResource.getIndex();
IndexDto index = indexResource.getIndex(httpServletRequest);
Assertions.assertThat(index.getLinks().getLinkBy("login")).matches(Optional::isPresent);
}
@Test
public void shouldRenderLoginInfoUrl() {
IndexDto index = indexResource.getIndex();
IndexDto index = indexResource.getIndex(httpServletRequest);
Assertions.assertThat(index.getLinks().getLinkBy("loginInfo")).isPresent();
}
@@ -103,21 +112,21 @@ public class IndexResourceTest {
public void shouldNotRenderLoginInfoUrlWhenNoUrlIsConfigured() {
configuration.setLoginInfoUrl("");
IndexDto index = indexResource.getIndex();
IndexDto index = indexResource.getIndex(httpServletRequest);
Assertions.assertThat(index.getLinks().getLinkBy("loginInfo")).isNotPresent();
}
@Test
public void shouldRenderSelfLinkForUnauthenticatedRequest() {
IndexDto index = indexResource.getIndex();
IndexDto index = indexResource.getIndex(httpServletRequest);
Assertions.assertThat(index.getLinks().getLinkBy("self")).matches(Optional::isPresent);
}
@Test
public void shouldRenderUiPluginsLinkForUnauthenticatedRequest() {
IndexDto index = indexResource.getIndex();
IndexDto index = indexResource.getIndex(httpServletRequest);
Assertions.assertThat(index.getLinks().getLinkBy("uiPlugins")).matches(Optional::isPresent);
}
@@ -125,7 +134,7 @@ public class IndexResourceTest {
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldRenderSelfLinkForAuthenticatedRequest() {
IndexDto index = indexResource.getIndex();
IndexDto index = indexResource.getIndex(httpServletRequest);
Assertions.assertThat(index.getLinks().getLinkBy("self")).matches(Optional::isPresent);
}
@@ -133,7 +142,7 @@ public class IndexResourceTest {
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldRenderUiPluginsLinkForAuthenticatedRequest() {
IndexDto index = indexResource.getIndex();
IndexDto index = indexResource.getIndex(httpServletRequest);
Assertions.assertThat(index.getLinks().getLinkBy("uiPlugins")).matches(Optional::isPresent);
}
@@ -141,7 +150,7 @@ public class IndexResourceTest {
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldRenderMeUrlForAuthenticatedRequest() {
IndexDto index = indexResource.getIndex();
IndexDto index = indexResource.getIndex(httpServletRequest);
Assertions.assertThat(index.getLinks().getLinkBy("me")).matches(Optional::isPresent);
}
@@ -149,7 +158,7 @@ public class IndexResourceTest {
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldRenderLogoutUrlForAuthenticatedRequest() {
IndexDto index = indexResource.getIndex();
IndexDto index = indexResource.getIndex(httpServletRequest);
Assertions.assertThat(index.getLinks().getLinkBy("logout")).matches(Optional::isPresent);
}
@@ -157,7 +166,7 @@ public class IndexResourceTest {
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldRenderRepositoriesForAuthenticatedRequest() {
IndexDto index = indexResource.getIndex();
IndexDto index = indexResource.getIndex(httpServletRequest);
Assertions.assertThat(index.getLinks().getLinkBy("repositories")).matches(Optional::isPresent);
}
@@ -165,7 +174,7 @@ public class IndexResourceTest {
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldNotRenderAdminLinksIfNotAuthorized() {
IndexDto index = indexResource.getIndex();
IndexDto index = indexResource.getIndex(httpServletRequest);
Assertions.assertThat(index.getLinks().getLinkBy("users")).matches(o -> !o.isPresent());
Assertions.assertThat(index.getLinks().getLinkBy("groups")).matches(o -> !o.isPresent());
@@ -175,7 +184,7 @@ public class IndexResourceTest {
@Test
@SubjectAware(username = "trillian", password = "secret")
public void shouldRenderAutoCompleteLinks() {
IndexDto index = indexResource.getIndex();
IndexDto index = indexResource.getIndex(httpServletRequest);
Assertions.assertThat(index.getLinks().getLinksBy("autocomplete"))
.extracting("name")
@@ -185,7 +194,7 @@ public class IndexResourceTest {
@Test
@SubjectAware(username = "user_without_autocomplete_permission", password = "secret")
public void userWithoutAutocompletePermissionShouldSeeAutoCompleteLinksOnlyForNamespaces() {
IndexDto index = indexResource.getIndex();
IndexDto index = indexResource.getIndex(httpServletRequest);
Assertions.assertThat(index.getLinks().getLinksBy("autocomplete"))
.extracting("name")
@@ -195,7 +204,7 @@ public class IndexResourceTest {
@Test
@SubjectAware(username = "dent", password = "secret")
public void shouldRenderAdminLinksIfAuthorized() {
IndexDto index = indexResource.getIndex();
IndexDto index = indexResource.getIndex(httpServletRequest);
Assertions.assertThat(index.getLinks().getLinkBy("users")).matches(Optional::isPresent);
Assertions.assertThat(index.getLinks().getLinkBy("groups")).matches(Optional::isPresent);
@@ -206,7 +215,7 @@ public class IndexResourceTest {
public void shouldGenerateVersion() {
when(scmContextProvider.getVersion()).thenReturn("v1");
IndexDto index = indexResource.getIndex();
IndexDto index = indexResource.getIndex(httpServletRequest);
Assertions.assertThat(index.getVersion()).isEqualTo("v1");
}

View File

@@ -0,0 +1,99 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.api.v2.resources;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.plugin.AvailablePlugin;
import sonia.scm.plugin.PluginSet;
import java.util.List;
import java.util.Locale;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
import static sonia.scm.plugin.PluginTestHelper.createAvailable;
@ExtendWith(MockitoExtension.class)
class PluginSetDtoMapperTest {
@Mock
private PluginDtoMapper pluginDtoMapper;
@InjectMocks
private PluginSetDtoMapper mapper;
@Test
void shouldMap() {
AvailablePlugin git = createAvailable("scm-git-plugin");
AvailablePlugin svn = createAvailable("scm-svn-plugin");
AvailablePlugin hg = createAvailable("scm-hg-plugin");
PluginDto gitDto = new PluginDto();
gitDto.setName("scm-git-plugin");
PluginDto svnDto = new PluginDto();
svnDto.setName("scm-svn-plugin");
PluginDto hgDto = new PluginDto();
hgDto.setName("scm-hg-plugin");
when(pluginDtoMapper.mapAvailable(git)).thenReturn(gitDto);
when(pluginDtoMapper.mapAvailable(svn)).thenReturn(svnDto);
when(pluginDtoMapper.mapAvailable(hg)).thenReturn(hgDto);
List<AvailablePlugin> availablePlugins = List.of(git, svn, hg);
PluginSet pluginSet = new PluginSet(
"my-plugin-set",
1,
ImmutableSet.of("scm-git-plugin", "scm-hg-plugin"),
ImmutableMap.of("en", new PluginSet.Description("My Plugin Set", List.of("this is awesome!"))),
ImmutableMap.of("standard", "base64image")
);
PluginSet pluginSet2 = new PluginSet(
"my-other-plugin-set",
0,
ImmutableSet.of("scm-svn-plugin", "scm-hg-plugin"),
ImmutableMap.of("en", new PluginSet.Description("My Plugin Set 2", List.of("this is also awesome!"))),
ImmutableMap.of("standard", "base64image")
);
ImmutableSet<PluginSet> pluginSets = ImmutableSet.of(pluginSet, pluginSet2);
List<PluginSetDto> dtos = mapper.map(pluginSets, availablePlugins, Locale.ENGLISH);
assertThat(dtos).hasSize(2);
PluginSetDto first = dtos.get(0);
assertThat(first.getSequence()).isZero();
assertThat(first.getName()).isEqualTo("My Plugin Set 2");
assertThat(first.getFeatures()).contains("this is also awesome!");
assertThat(first.getImages()).isNotEmpty();
assertThat(first.getPlugins()).hasSize(2);
assertThat(dtos.get(1).getSequence()).isEqualTo(1);
}
}

View File

@@ -0,0 +1,186 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.api.v2.resources;
import com.github.sdorra.shiro.SubjectAware;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import de.otto.edison.hal.Embedded;
import de.otto.edison.hal.Links;
import org.apache.commons.lang.StringUtils;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Answers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.lifecycle.PluginWizardStartupAction;
import sonia.scm.lifecycle.PrivilegedStartupAction;
import sonia.scm.plugin.AvailablePlugin;
import sonia.scm.plugin.PluginManager;
import sonia.scm.plugin.PluginSet;
import sonia.scm.security.AccessTokenCookieIssuer;
import sonia.scm.web.RestDispatcher;
import sonia.scm.web.security.AdministrationContext;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Locale;
import static java.lang.String.format;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.emptyList;
import static java.util.Collections.singleton;
import static org.assertj.core.api.Assertions.assertThat;
import static org.jboss.resteasy.mock.MockHttpRequest.post;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static sonia.scm.plugin.PluginTestHelper.createAvailable;
@SubjectAware(
configuration = "classpath:sonia/scm/repository/shiro.ini"
)
@ExtendWith(MockitoExtension.class)
class PluginWizardStartupResourceTest {
@Mock
private PluginWizardStartupAction startupAction;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private ResourceLinks resourceLinks;
@Mock
private PluginManager pluginManager;
@Mock
private AccessTokenCookieIssuer cookieIssuer;
@Mock
private PluginSetDtoMapper pluginSetDtoMapper;
@Mock
private AdministrationContext context;
private final RestDispatcher dispatcher = new RestDispatcher();
private final MockHttpResponse response = new MockHttpResponse();
@InjectMocks
private PluginWizardStartupResource resource;
@BeforeEach
void setUpMocks() {
dispatcher.addSingletonResource(new InitializationResource(singleton(resource)));
lenient().when(startupAction.name()).thenReturn("pluginWizard");
}
@Test
void shouldFailWhenActionIsDone() throws URISyntaxException {
when(startupAction.done()).thenReturn(true);
MockHttpRequest request =
post("/v2/initialization/pluginWizard")
.contentType("application/json")
.content(createInput("my-plugin-set"));
dispatcher.invoke(request, response);
assertThat(response.getStatus()).isEqualTo(400);
}
@Test
void shouldInstallPluginSets() throws URISyntaxException {
when(startupAction.done()).thenReturn(false);
MockHttpRequest request =
post("/v2/initialization/pluginWizard")
.contentType("application/json")
.content(createInput("my-plugin-set", "my-other-plugin-set"));
dispatcher.invoke(request, response);
verify(cookieIssuer).invalidate(any(), any());
verify(pluginManager).installPluginSets(ImmutableSet.of("my-plugin-set", "my-other-plugin-set"), true);
}
@Test
void shouldSetupIndex() {
AvailablePlugin git = createAvailable("scm-git-plugin");
AvailablePlugin svn = createAvailable("scm-svn-plugin");
AvailablePlugin hg = createAvailable("scm-hg-plugin");
List<AvailablePlugin> availablePlugins = List.of(git, svn, hg);
when(pluginManager.getAvailable()).thenReturn(availablePlugins);
doAnswer(invocation -> {
invocation.getArgument(0, PrivilegedStartupAction.class).run();
return null;
}).when(context).runAsAdmin(any(PrivilegedStartupAction.class));
PluginSet pluginSet = new PluginSet(
"my-plugin-set",
0,
ImmutableSet.of("scm-git-plugin", "scm-hg-plugin"),
ImmutableMap.of("en", new PluginSet.Description("My Plugin Set", List.of("this is awesome!"))),
ImmutableMap.of("standard", "base64image")
);
PluginSet pluginSet2 = new PluginSet(
"my-other-plugin-set",
0,
ImmutableSet.of("scm-svn-plugin", "scm-hg-plugin"),
ImmutableMap.of("en", new PluginSet.Description("My Plugin Set", List.of("this is awesome!"))),
ImmutableMap.of("standard", "base64image")
);
ImmutableSet<PluginSet> pluginSets = ImmutableSet.of(pluginSet, pluginSet2);
when(pluginManager.getPluginSets()).thenReturn(pluginSets);
when(pluginSetDtoMapper.map(pluginSets, availablePlugins, Locale.ENGLISH)).thenReturn(emptyList());
when(resourceLinks.pluginWizard().indexLink("pluginWizard")).thenReturn("http://index.link");
Embedded.Builder embeddedBuilder = new Embedded.Builder();
Links.Builder linksBuilder = new Links.Builder();
resource.setupIndex(linksBuilder, embeddedBuilder, Locale.ENGLISH);
Embedded embedded = embeddedBuilder.build();
Links links = linksBuilder.build();
assertThat(links.getLinkBy("installPluginSets")).isPresent();
assertThat(embedded.hasItem("pluginSets")).isTrue();
}
private byte[] createInput(String... pluginSetIds) {
String format = pluginSetIds.length > 0 ? "'%s'" : "%s";
return json(format("{'pluginSetIds': [" + format + "]}", StringUtils.join(pluginSetIds, "','")));
}
private byte[] json(String s) {
return s.replaceAll("'", "\"").getBytes(UTF_8);
}
}

View File

@@ -0,0 +1,115 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.initialization;
import org.apache.shiro.authc.AuthenticationException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Answers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.security.AccessToken;
import sonia.scm.security.AccessTokenBuilder;
import sonia.scm.security.AccessTokenBuilderFactory;
import sonia.scm.security.AccessTokenCookieIssuer;
import sonia.scm.web.security.AdministrationContext;
import sonia.scm.web.security.PrivilegedAction;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class InitializationAuthenticationServiceTest {
@Mock
private AccessTokenBuilderFactory tokenBuilderFactory;
@Mock(answer = Answers.RETURNS_SELF)
private AccessTokenBuilder tokenBuilder;
@Mock
private AccessToken token;
@Mock
private AccessTokenCookieIssuer cookieIssuer;
@Mock
private InitializationCookieIssuer initializationCookieIssuer;
@Mock
private AdministrationContext administrationContext;
@InjectMocks
private InitializationAuthenticationService service;
@Test
void shouldNotThrowExceptionIfTokenIsValid() {
when(token.getSubject()).thenReturn("SCM-INIT");
service.validateToken(token);
}
@Test
void shouldThrowExceptionIfTokenIsInvalid() {
when(token.getSubject()).thenReturn("FAKE");
assertThrows(AuthenticationException.class, () -> service.validateToken(token));
}
@Test
void shouldSetPermissionForVirtualInitializationUserInAdminContext() {
service.setPermissions();
verify(administrationContext).runAsAdmin(any(PrivilegedAction.class));
}
@Test
void shouldAuthenticate() {
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
when(tokenBuilderFactory.create()).thenReturn(tokenBuilder);
AccessToken accessToken = mock(AccessToken.class);
when(tokenBuilder.build()).thenReturn(accessToken);
service.authenticate(request, response);
verify(initializationCookieIssuer)
.authenticateForInitialization(request, response, accessToken);
verify(tokenBuilder).subject("SCM-INIT");
}
@Test
void shouldInvalidateCookies() {
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
service.invalidateCookies(request, response);
verify(cookieIssuer).invalidate(request, response);
}
}

View File

@@ -0,0 +1,65 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.initialization;
import org.apache.shiro.authc.AuthenticationToken;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static sonia.scm.initialization.InitializationWebTokenGenerator.INIT_TOKEN_HEADER;
@ExtendWith(MockitoExtension.class)
class InitializationWebTokenGeneratorTest {
private static final String INIT_TOKEN = "my_init_token";
private final InitializationWebTokenGenerator generator = new InitializationWebTokenGenerator();
@Test
void shouldReturnNullTokenIfCookieIsMissing() {
HttpServletRequest request = mock(HttpServletRequest.class);
AuthenticationToken token = generator.createToken(request);
assertThat(token).isNull();
}
@Test
void shouldGenerateCookieToken() {
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getCookies()).thenReturn(new Cookie[]{new Cookie(INIT_TOKEN_HEADER, INIT_TOKEN)});
AuthenticationToken token = generator.createToken(request);
assertThat(token.getCredentials()).isEqualTo(INIT_TOKEN);
assertThat(token.getPrincipal()).isEqualTo("SCM_INIT");
}
}

View File

@@ -0,0 +1,73 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.lifecycle;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.plugin.PluginSetConfigStore;
import sonia.scm.plugin.PluginSetsConfig;
import java.util.Optional;
@ExtendWith(MockitoExtension.class)
class PluginWizardStartupActionTest {
@Mock
private PluginSetConfigStore pluginSetConfigStore;
@InjectMocks
private PluginWizardStartupAction startupAction;
@BeforeEach
void setup() {
System.clearProperty(AdminAccountStartupAction.INITIAL_PASSWORD_PROPERTY);
}
@Test
void shouldNotBeDoneByDefault() {
Assertions.assertThat(startupAction.done()).isFalse();
}
@Test
void shouldBeDoneIfInitialPasswordIsSet() {
System.setProperty(AdminAccountStartupAction.INITIAL_PASSWORD_PROPERTY, "secret");
Assertions.assertThat(startupAction.done()).isTrue();
}
@Test
void shouldBeDoneIfConfigIsAlreadySet() {
Mockito.when(pluginSetConfigStore.getPluginSets()).thenReturn(Optional.of(new PluginSetsConfig()));
Assertions.assertThat(startupAction.done()).isTrue();
}
}

View File

@@ -25,6 +25,7 @@
package sonia.scm.plugin;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.subject.Subject;
@@ -38,6 +39,7 @@ import org.junit.jupiter.api.io.TempDir;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.NotFoundException;
import sonia.scm.ScmConstraintViolationException;
@@ -49,6 +51,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import static java.util.Arrays.asList;
import static java.util.Collections.singleton;
@@ -83,6 +86,9 @@ class DefaultPluginManagerTest {
@Mock
private Restarter restarter;
@Mock
private PluginSetConfigStore pluginSetConfigStore;
@Mock
private ScmEventBus eventBus;
@@ -110,7 +116,7 @@ class DefaultPluginManagerTest {
@BeforeEach
void setUpObjectUnderTest() {
manager = new DefaultPluginManager(
loader, center, installer, restarter, eventBus, plugins -> context
loader, center, installer, restarter, eventBus, plugins -> context, pluginSetConfigStore
);
}
@@ -162,7 +168,7 @@ class DefaultPluginManagerTest {
AvailablePlugin review = createAvailable("scm-review-plugin");
AvailablePlugin git = createAvailable("scm-git-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review, git));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(review, git));
List<AvailablePlugin> available = manager.getAvailable();
assertThat(available).containsOnly(review, git);
@@ -175,7 +181,7 @@ class DefaultPluginManagerTest {
AvailablePlugin review = createAvailable("scm-review-plugin");
AvailablePlugin git = createAvailable("scm-git-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review, git));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(review, git));
List<AvailablePlugin> available = manager.getAvailable();
assertThat(available).containsOnly(review);
@@ -185,7 +191,7 @@ class DefaultPluginManagerTest {
void shouldReturnAvailable() {
AvailablePlugin review = createAvailable("scm-review-plugin");
AvailablePlugin git = createAvailable("scm-git-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review, git));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(review, git));
Optional<AvailablePlugin> available = manager.getAvailable("scm-git-plugin");
assertThat(available).contains(git);
@@ -194,7 +200,7 @@ class DefaultPluginManagerTest {
@Test
void shouldReturnEmptyForNonExistingAvailable() {
AvailablePlugin review = createAvailable("scm-review-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(review));
Optional<AvailablePlugin> available = manager.getAvailable("scm-git-plugin");
assertThat(available).isEmpty();
@@ -206,7 +212,7 @@ class DefaultPluginManagerTest {
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(installedGit));
AvailablePlugin git = createAvailable("scm-git-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(git));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(git));
Optional<AvailablePlugin> available = manager.getAvailable("scm-git-plugin");
assertThat(available).isEmpty();
@@ -215,7 +221,7 @@ class DefaultPluginManagerTest {
@Test
void shouldInstallThePlugin() {
AvailablePlugin git = createAvailable("scm-git-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(git));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(git));
manager.install("scm-git-plugin", false);
@@ -228,7 +234,7 @@ class DefaultPluginManagerTest {
AvailablePlugin review = createAvailable("scm-review-plugin");
when(review.getDescriptor().getDependencies()).thenReturn(ImmutableSet.of("scm-mail-plugin"));
AvailablePlugin mail = createAvailable("scm-mail-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review, mail));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(review, mail));
manager.install("scm-review-plugin", false);
@@ -241,7 +247,7 @@ class DefaultPluginManagerTest {
AvailablePlugin review = createAvailable("scm-review-plugin");
when(review.getDescriptor().getDependencies()).thenReturn(ImmutableSet.of("scm-mail-plugin"));
AvailablePlugin mail = createAvailable("scm-mail-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review, mail));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(review, mail));
InstalledPlugin installedMail = createInstalled("scm-mail-plugin");
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(installedMail));
@@ -259,7 +265,7 @@ class DefaultPluginManagerTest {
AvailablePlugin review = createAvailable("scm-review-plugin");
when(review.getDescriptor().getDependencies()).thenReturn(ImmutableSet.of("scm-mail-plugin"));
AvailablePlugin mail = createAvailable("scm-mail-plugin", "1.1.0");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review, mail));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(review, mail));
InstalledPlugin installedMail = createInstalled("scm-mail-plugin", "1.0.0");
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(installedMail));
@@ -275,7 +281,7 @@ class DefaultPluginManagerTest {
AvailablePlugin review = createAvailable("scm-review-plugin");
when(review.getDescriptor().getOptionalDependencies()).thenReturn(ImmutableSet.of("scm-mail-plugin"));
AvailablePlugin mail = createAvailable("scm-mail-plugin", "1.1.0");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review, mail));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(review, mail));
InstalledPlugin installedMail = createInstalled("scm-mail-plugin", "1.0.0");
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(installedMail));
@@ -291,7 +297,7 @@ class DefaultPluginManagerTest {
AvailablePlugin review = createAvailable("scm-review-plugin");
when(review.getDescriptor().getOptionalDependencies()).thenReturn(ImmutableSet.of("scm-mail-plugin"));
AvailablePlugin mail = createAvailable("scm-mail-plugin", "1.1.0");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review, mail));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(review, mail));
manager.install("scm-review-plugin", false);
@@ -306,7 +312,7 @@ class DefaultPluginManagerTest {
AvailablePlugin mail = createAvailable("scm-mail-plugin");
when(mail.getDescriptor().getDependencies()).thenReturn(ImmutableSet.of("scm-notification-plugin"));
AvailablePlugin notification = createAvailable("scm-notification-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review, mail, notification));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(review, mail, notification));
PendingPluginInstallation pendingNotification = mock(PendingPluginInstallation.class);
doReturn(pendingNotification).when(installer).install(context, notification);
@@ -328,7 +334,7 @@ class DefaultPluginManagerTest {
when(review.getDescriptor().getDependencies()).thenReturn(ImmutableSet.of("scm-mail-plugin"));
AvailablePlugin mail = createAvailable("scm-mail-plugin");
when(mail.getDescriptor().getDependencies()).thenReturn(ImmutableSet.of("scm-notification-plugin"));
when(center.getAvailable()).thenReturn(ImmutableSet.of(review, mail));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(review, mail));
assertThrows(NotFoundException.class, () -> manager.install("scm-review-plugin", false));
@@ -338,7 +344,7 @@ class DefaultPluginManagerTest {
@Test
void shouldSendRestartEventAfterInstallation() {
AvailablePlugin git = createAvailable("scm-git-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(git));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(git));
manager.install("scm-git-plugin", true);
@@ -358,7 +364,7 @@ class DefaultPluginManagerTest {
@Test
void shouldNotInstallAlreadyPendingPlugins() {
AvailablePlugin review = createAvailable("scm-review-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(review));
manager.install("scm-review-plugin", false);
manager.install("scm-review-plugin", false);
@@ -369,7 +375,7 @@ class DefaultPluginManagerTest {
@Test
void shouldSendRestartEvent() {
AvailablePlugin review = createAvailable("scm-review-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(review));
manager.install("scm-review-plugin", false);
manager.executePendingAndRestart();
@@ -387,7 +393,7 @@ class DefaultPluginManagerTest {
@Test
void shouldReturnSingleAvailableAsPending() {
AvailablePlugin review = createAvailable("scm-review-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(review));
manager.install("scm-review-plugin", false);
@@ -398,7 +404,7 @@ class DefaultPluginManagerTest {
@Test
void shouldReturnAvailableAsPending() {
AvailablePlugin review = createAvailable("scm-review-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(review));
manager.install("scm-review-plugin", false);
@@ -514,7 +520,7 @@ class DefaultPluginManagerTest {
when(reviewPlugin.getDescriptor().getDependencies()).thenReturn(singleton("scm-mail-plugin"));
when(loader.getInstalledPlugins()).thenReturn(singletonList(mailPlugin));
when(center.getAvailable()).thenReturn(singleton(reviewPlugin));
when(center.getAvailablePlugins()).thenReturn(singleton(reviewPlugin));
manager.computeInstallationDependencies();
@@ -546,7 +552,7 @@ class DefaultPluginManagerTest {
doNothing().when(mailPlugin).setMarkedForUninstall(uninstallCaptor.capture());
AvailablePlugin git = createAvailable("scm-git-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(git));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(git));
PendingPluginInstallation gitPendingPluginInformation = mock(PendingPluginInstallation.class);
when(installer.install(context, git)).thenReturn(gitPendingPluginInformation);
@@ -577,7 +583,7 @@ class DefaultPluginManagerTest {
AvailablePlugin newMailPlugin = createAvailable("scm-mail-plugin", "2.0.0");
AvailablePlugin newReviewPlugin = createAvailable("scm-review-plugin", "2.0.0");
when(center.getAvailable()).thenReturn(ImmutableSet.of(newMailPlugin, newReviewPlugin));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(newMailPlugin, newReviewPlugin));
manager.updateAll();
@@ -593,7 +599,7 @@ class DefaultPluginManagerTest {
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(scriptPlugin));
AvailablePlugin oldScriptPlugin = createAvailable("scm-script-plugin", "0.9");
when(center.getAvailable()).thenReturn(ImmutableSet.of(oldScriptPlugin));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(oldScriptPlugin));
manager.updateAll();
@@ -603,7 +609,7 @@ class DefaultPluginManagerTest {
@Test
void shouldFirePluginEventOnInstallation() {
AvailablePlugin review = createAvailable("scm-review-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(review));
manager.install("scm-review-plugin", false);
@@ -616,7 +622,7 @@ class DefaultPluginManagerTest {
@Test
void shouldFirePluginEventOnFailedInstallation() {
AvailablePlugin review = createAvailable("scm-review-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(review));
doThrow(new PluginDownloadException(review, new IOException())).when(installer).install(context, review);
assertThrows(PluginDownloadException.class, () -> manager.install("scm-review-plugin", false));
@@ -637,7 +643,7 @@ class DefaultPluginManagerTest {
when(jenkins.getDescriptor().getDependencies()).thenReturn(ImmutableSet.of("scm-el-plugin"));
when(webhook.getDescriptor().getDependencies()).thenReturn(ImmutableSet.of("scm-el-plugin"));
AvailablePlugin el = createAvailable("scm-el-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(jenkins, el, webhook));
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(jenkins, el, webhook));
manager.install("scm-jenkins-plugin", false);
manager.install("scm-webhook-plugin", false);
@@ -650,6 +656,55 @@ class DefaultPluginManagerTest {
assertThat(pluginInstallationContext.find("scm-webhook-plugin")).isPresent();
assertThat(pluginInstallationContext.find("scm-el-plugin")).isPresent();
}
@Test
void shouldGetPluginSets() {
PluginSet pluginSet = new PluginSet(
"my-plugin-set",
0,
ImmutableSet.of("scm-jenkins-plugin", "scm-webhook-plugin", "scm-el-plugin"),
ImmutableMap.of("en", new PluginSet.Description("My Plugin Set", List.of("this is awesome!"))),
ImmutableMap.of("standard", "base64image")
);
when(center.getAvailablePluginSets()).thenReturn(ImmutableSet.of(pluginSet));
Set<PluginSet> pluginSets = manager.getPluginSets();
assertThat(pluginSets).containsExactly(pluginSet);
}
@Test
void shouldInstallPluginSets() {
AvailablePlugin git = createAvailable("scm-git-plugin");
AvailablePlugin svn = createAvailable("scm-svn-plugin");
AvailablePlugin hg = createAvailable("scm-hg-plugin");
when(center.getAvailablePlugins()).thenReturn(ImmutableSet.of(git, svn, hg));
PluginSet pluginSet = new PluginSet(
"my-plugin-set",
0,
ImmutableSet.of("scm-git-plugin", "scm-hg-plugin"),
ImmutableMap.of("en", new PluginSet.Description("My Plugin Set", List.of("this is awesome!"))),
ImmutableMap.of("standard", "base64image")
);
PluginSet pluginSet2 = new PluginSet(
"my-other-plugin-set",
0,
ImmutableSet.of("scm-svn-plugin", "scm-hg-plugin"),
ImmutableMap.of("en", new PluginSet.Description("My Plugin Set", List.of("this is awesome!"))),
ImmutableMap.of("standard", "base64image")
);
when(center.getAvailablePluginSets()).thenReturn(ImmutableSet.of(pluginSet, pluginSet2));
manager.installPluginSets(ImmutableSet.of("my-plugin-set", "my-other-plugin-set"), false);
verify(pluginSetConfigStore).setPluginSets(new PluginSetsConfig(ImmutableSet.of("my-plugin-set", "my-other-plugin-set")));
verify(installer, Mockito.times(1)).install(context, git);
verify(installer, Mockito.times(1)).install(context, hg);
verify(installer, Mockito.times(1)).install(context, svn);
verify(restarter, never()).restart(any(), any());
}
}
@Nested
@@ -672,6 +727,7 @@ class DefaultPluginManagerTest {
assertThrows(AuthorizationException.class, () -> manager.getInstalled("test"));
assertThrows(AuthorizationException.class, () -> manager.getAvailable());
assertThrows(AuthorizationException.class, () -> manager.getAvailable("test"));
assertThrows(AuthorizationException.class, () -> manager.getPluginSets());
}
}
@@ -695,6 +751,12 @@ class DefaultPluginManagerTest {
assertThrows(AuthorizationException.class, () -> manager.install("test", false));
}
@Test
void shouldThrowAuthorizationExceptionsForInstallPluginSetsMethod() {
ImmutableSet<String> pluginSetIds = ImmutableSet.of("test");
assertThrows(AuthorizationException.class, () -> manager.installPluginSets(pluginSetIds, false));
}
@Test
void shouldThrowAuthorizationExceptionsForUninstallMethod() {
assertThrows(AuthorizationException.class, () -> manager.uninstall("test", false));

View File

@@ -33,17 +33,16 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
import static sonia.scm.plugin.PluginCenterDto.Condition;
import static sonia.scm.plugin.PluginCenterDto.Link;
import static sonia.scm.plugin.PluginCenterDto.Plugin;
import static sonia.scm.plugin.PluginCenterDto.*;
@ExtendWith(MockitoExtension.class)
class PluginCenterDtoMapperTest {
@@ -72,8 +71,19 @@ class PluginCenterDtoMapperTest {
ImmutableMap.of("download", new Link("http://download.hitchhiker.com"))
);
PluginCenterDto.PluginSet pluginSet = new PluginCenterDto.PluginSet(
"my-plugin-set",
">2.0.0",
0,
ImmutableSet.of("scm-review-plugin"),
ImmutableMap.of("en", new PluginCenterDto.Description("My Plugin Set", List.of("hello world"))),
ImmutableMap.of("standard", "base64image")
);
when(dto.getEmbedded().getPlugins()).thenReturn(Collections.singletonList(plugin));
AvailablePluginDescriptor descriptor = mapper.map(dto).iterator().next().getDescriptor();
when(dto.getEmbedded().getPluginSets()).thenReturn(Collections.singletonList(pluginSet));
PluginCenterResult mapped = mapper.map(dto);
AvailablePluginDescriptor descriptor = mapped.getPlugins().iterator().next().getDescriptor();
PluginInformation information = descriptor.getInformation();
PluginCondition condition = descriptor.getCondition();
@@ -88,6 +98,14 @@ class PluginCenterDtoMapperTest {
assertThat(condition.getOs().iterator().next()).isEqualTo(plugin.getConditions().getOs().iterator().next());
assertThat(information.getDescription()).isEqualTo(plugin.getDescription());
assertThat(information.getName()).isEqualTo(plugin.getName());
PluginSet mappedPluginSet = mapped.getPluginSets().iterator().next();
assertThat(mappedPluginSet.getId()).isEqualTo(pluginSet.getId());
assertThat(mappedPluginSet.getSequence()).isEqualTo(pluginSet.getSequence());
assertThat(mappedPluginSet.getPlugins()).hasSize(pluginSet.getPlugins().size());
assertThat(mappedPluginSet.getImages()).isNotEmpty();
assertThat(mappedPluginSet.getDescriptions()).isNotEmpty();
}
@Test
@@ -126,7 +144,8 @@ class PluginCenterDtoMapperTest {
when(dto.getEmbedded().getPlugins()).thenReturn(Arrays.asList(plugin1, plugin2));
Set<AvailablePlugin> resultSet = mapper.map(dto);
PluginCenterResult pluginCenterResult = mapper.map(dto);
Set<AvailablePlugin> resultSet = pluginCenterResult.getPlugins();
PluginInformation pluginInformation1 = findPlugin(resultSet, plugin1.getName());
PluginInformation pluginInformation2 = findPlugin(resultSet, plugin2.getName());

View File

@@ -43,7 +43,6 @@ import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static sonia.scm.plugin.Tracing.SPAN_KIND;
@ExtendWith(MockitoExtension.class)
class PluginCenterLoaderTest {
@@ -71,12 +70,15 @@ class PluginCenterLoaderTest {
@Test
void shouldFetch() throws IOException {
Set<AvailablePlugin> plugins = Collections.emptySet();
Set<PluginSet> pluginSets = Collections.emptySet();
PluginCenterDto dto = new PluginCenterDto();
PluginCenterResult pluginCenterResult = new PluginCenterResult(plugins, pluginSets);
when(request().contentFromJson(PluginCenterDto.class)).thenReturn(dto);
when(mapper.map(dto)).thenReturn(plugins);
when(mapper.map(dto)).thenReturn(pluginCenterResult);
Set<AvailablePlugin> fetched = loader.load(PLUGIN_URL);
assertThat(fetched).isSameAs(plugins);
PluginCenterResult fetched = loader.load(PLUGIN_URL);
assertThat(fetched.getPlugins()).isSameAs(plugins);
assertThat(fetched.getPluginSets()).isSameAs(pluginSets);
}
private AdvancedHttpResponse request() throws IOException {
@@ -91,8 +93,9 @@ class PluginCenterLoaderTest {
when(client.get(PLUGIN_URL)).thenReturn(request);
when(request.request()).thenThrow(new IOException("failed to fetch"));
Set<AvailablePlugin> fetch = loader.load(PLUGIN_URL);
assertThat(fetch).isEmpty();
PluginCenterResult fetch = loader.load(PLUGIN_URL);
assertThat(fetch.getPlugins()).isEmpty();
assertThat(fetch.getPluginSets()).isEmpty();
}
@Test
@@ -119,8 +122,9 @@ class PluginCenterLoaderTest {
private Set<AvailablePlugin> mockResponse() throws IOException {
PluginCenterDto dto = new PluginCenterDto();
Set<AvailablePlugin> plugins = Collections.emptySet();
Set<PluginSet> pluginSets = Collections.emptySet();
when(request().contentFromJson(PluginCenterDto.class)).thenReturn(dto);
when(mapper.map(dto)).thenReturn(plugins);
when(mapper.map(dto)).thenReturn(new PluginCenterResult(plugins, pluginSets));
return plugins;
}

View File

@@ -24,21 +24,16 @@
package sonia.scm.plugin;
import com.google.common.collect.ImmutableSet;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.SCMContextProvider;
import sonia.scm.cache.CacheManager;
import sonia.scm.cache.MapCacheManager;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.net.ahc.AdvancedHttpClient;
import sonia.scm.util.SystemUtil;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -81,36 +76,52 @@ class PluginCenterTest {
@Test
void shouldFetchPlugins() {
Set<AvailablePlugin> plugins = new HashSet<>();
when(loader.load(PLUGIN_URL_BASE + "2.0.0")).thenReturn(plugins);
Set<PluginSet> pluginSets = new HashSet<>();
assertThat(pluginCenter.getAvailable()).isSameAs(plugins);
PluginCenterResult pluginCenterResult = new PluginCenterResult(plugins, pluginSets);
when(loader.load(PLUGIN_URL_BASE + "2.0.0")).thenReturn(pluginCenterResult);
assertThat(pluginCenter.getAvailablePlugins()).isSameAs(plugins);
assertThat(pluginCenter.getAvailablePluginSets()).isSameAs(pluginSets);
}
@Test
@SuppressWarnings("unchecked")
void shouldCache() {
Set<AvailablePlugin> first = new HashSet<>();
when(loader.load(anyString())).thenReturn(first, new HashSet<>());
Set<AvailablePlugin> plugins = new HashSet<>();
Set<PluginSet> pluginSets = new HashSet<>();
assertThat(pluginCenter.getAvailable()).isSameAs(first);
assertThat(pluginCenter.getAvailable()).isSameAs(first);
PluginCenterResult first = new PluginCenterResult(plugins, pluginSets);
when(loader.load(anyString())).thenReturn(first, new PluginCenterResult(Collections.emptySet(), Collections.emptySet()));
assertThat(pluginCenter.getAvailablePlugins()).isSameAs(plugins);
assertThat(pluginCenter.getAvailablePlugins()).isSameAs(plugins);
assertThat(pluginCenter.getAvailablePluginSets()).isSameAs(pluginSets);
}
@Test
@SuppressWarnings("unchecked")
void shouldClearCache() {
Set<AvailablePlugin> first = new HashSet<>();
when(loader.load(anyString())).thenReturn(first, new HashSet<>());
Set<AvailablePlugin> plugins = new HashSet<>();
Set<PluginSet> pluginSets = new HashSet<>();
assertThat(pluginCenter.getAvailable()).isSameAs(first);
PluginCenterResult first = new PluginCenterResult(plugins, pluginSets);
when(loader.load(anyString())).thenReturn(first, new PluginCenterResult(Collections.emptySet(), Collections.emptySet()));
assertThat(pluginCenter.getAvailablePlugins()).isSameAs(plugins);
assertThat(pluginCenter.getAvailablePluginSets()).isSameAs(pluginSets);
pluginCenter.handle(new PluginCenterLoginEvent(null));
assertThat(pluginCenter.getAvailable()).isNotSameAs(first);
assertThat(pluginCenter.getAvailablePlugins()).isNotSameAs(plugins);
assertThat(pluginCenter.getAvailablePluginSets()).isNotSameAs(pluginSets);
}
@Test
void shouldLoadOnRefresh() {
Set<AvailablePlugin> plugins = new HashSet<>();
when(loader.load(PLUGIN_URL_BASE + "2.0.0")).thenReturn(plugins);
Set<PluginSet> pluginSets = new HashSet<>();
PluginCenterResult pluginCenterResult = new PluginCenterResult(plugins, pluginSets);
when(loader.load(PLUGIN_URL_BASE + "2.0.0")).thenReturn(pluginCenterResult);
pluginCenter.refresh();