handle pending plugin installations

This commit is contained in:
Sebastian Sdorra
2019-08-21 12:49:15 +02:00
parent 25cb0d6a25
commit 9514a94492
17 changed files with 292 additions and 66 deletions

View File

@@ -117,7 +117,7 @@ class AvailablePluginResourceTest {
PluginDto pluginDto = new PluginDto();
pluginDto.setName("pluginName");
when(mapper.map(plugin)).thenReturn(pluginDto);
when(mapper.mapAvailable(plugin)).thenReturn(pluginDto);
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/available/pluginName");
request.accept(VndMediaType.PLUGIN);

View File

@@ -110,7 +110,7 @@ class InstalledPluginResourceTest {
PluginDto pluginDto = new PluginDto();
pluginDto.setName("pluginName");
when(mapper.map(installedPlugin)).thenReturn(pluginDto);
when(mapper.mapInstalled(installedPlugin)).thenReturn(pluginDto);
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/installed/pluginName");
request.accept(VndMediaType.PLUGIN);

View File

@@ -1,15 +1,20 @@
package sonia.scm.api.v2.resources;
import com.google.common.collect.ImmutableSet;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ThreadContext;
import org.junit.jupiter.api.AfterEach;
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.Mockito;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.plugin.Plugin;
import sonia.scm.plugin.PluginDescriptor;
import sonia.scm.plugin.AvailablePlugin;
import sonia.scm.plugin.AvailablePluginDescriptor;
import sonia.scm.plugin.InstalledPlugin;
import sonia.scm.plugin.PluginInformation;
import sonia.scm.plugin.PluginState;
import java.net.URI;
@@ -26,6 +31,19 @@ class PluginDtoMapperTest {
@InjectMocks
private PluginDtoMapperImpl mapper;
@Mock
private Subject subject;
@BeforeEach
void bindSubject() {
ThreadContext.bind(subject);
}
@AfterEach
void unbindSubject() {
ThreadContext.unbindSubject();
}
@Test
void shouldMapInformation() {
PluginInformation information = createPluginInformation();
@@ -54,27 +72,42 @@ class PluginDtoMapperTest {
@Test
void shouldAppendInstalledSelfLink() {
Plugin plugin = createPlugin(PluginState.INSTALLED);
InstalledPlugin plugin = createInstalled();
PluginDto dto = mapper.map(plugin);
PluginDto dto = mapper.mapInstalled(plugin);
assertThat(dto.getLinks().getLinkBy("self").get().getHref())
.isEqualTo("https://hitchhiker.com/v2/plugins/installed/scm-cas-plugin");
}
private InstalledPlugin createInstalled(PluginInformation information) {
InstalledPlugin plugin = mock(InstalledPlugin.class, Answers.RETURNS_DEEP_STUBS);
when(plugin.getDescriptor().getInformation()).thenReturn(information);
return plugin;
}
@Test
void shouldAppendAvailableSelfLink() {
Plugin plugin = createPlugin(PluginState.AVAILABLE);
AvailablePlugin plugin = createAvailable();
PluginDto dto = mapper.map(plugin);
PluginDto dto = mapper.mapAvailable(plugin);
assertThat(dto.getLinks().getLinkBy("self").get().getHref())
.isEqualTo("https://hitchhiker.com/v2/plugins/available/scm-cas-plugin");
}
@Test
void shouldAppendInstallLink() {
Plugin plugin = createPlugin(PluginState.AVAILABLE);
void shouldNotAppendInstallLinkWithoutPermissions() {
AvailablePlugin plugin = createAvailable();
PluginDto dto = mapper.map(plugin);
PluginDto dto = mapper.mapAvailable(plugin);
assertThat(dto.getLinks().getLinkBy("install")).isEmpty();
}
@Test
void shouldAppendInstallLink() {
when(subject.isPermitted("plugin:manage")).thenReturn(true);
AvailablePlugin plugin = createAvailable();
PluginDto dto = mapper.mapAvailable(plugin);
assertThat(dto.getLinks().getLinkBy("install").get().getHref())
.isEqualTo("https://hitchhiker.com/v2/plugins/available/scm-cas-plugin/install");
}
@@ -83,31 +116,32 @@ class PluginDtoMapperTest {
void shouldReturnMiscellaneousIfCategoryIsNull() {
PluginInformation information = createPluginInformation();
information.setCategory(null);
Plugin plugin = createPlugin(information, PluginState.AVAILABLE);
PluginDto dto = mapper.map(plugin);
AvailablePlugin plugin = createAvailable(information);
PluginDto dto = mapper.mapAvailable(plugin);
assertThat(dto.getCategory()).isEqualTo("Miscellaneous");
}
@Test
void shouldAppendDependencies() {
Plugin plugin = createPlugin(PluginState.AVAILABLE);
AvailablePlugin plugin = createAvailable();
when(plugin.getDescriptor().getDependencies()).thenReturn(ImmutableSet.of("one", "two"));
PluginDto dto = mapper.map(plugin);
PluginDto dto = mapper.mapAvailable(plugin);
assertThat(dto.getDependencies()).containsOnly("one", "two");
}
private Plugin createPlugin(PluginState state) {
return createPlugin(createPluginInformation(), state);
private InstalledPlugin createInstalled() {
return createInstalled(createPluginInformation());
}
private Plugin createPlugin(PluginInformation information, PluginState state) {
Plugin plugin = Mockito.mock(Plugin.class);
when(plugin.getState()).thenReturn(state);
PluginDescriptor descriptor = mock(PluginDescriptor.class);
private AvailablePlugin createAvailable() {
return createAvailable(createPluginInformation());
}
private AvailablePlugin createAvailable(PluginInformation information) {
AvailablePluginDescriptor descriptor = mock(AvailablePluginDescriptor.class);
when(descriptor.getInformation()).thenReturn(information);
when(plugin.getDescriptor()).thenReturn(descriptor);
return plugin;
return new AvailablePlugin(descriptor);
}
}

View File

@@ -11,6 +11,7 @@ import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@@ -22,6 +23,7 @@ import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.in;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;
@@ -46,6 +48,14 @@ class DefaultPluginManagerTest {
@Mock
private Subject subject;
@BeforeEach
void mockInstaller() {
lenient().when(installer.install(any())).then(ic -> {
AvailablePlugin plugin = ic.getArgument(0);
return new PendingPluginInstallation(plugin.install(), null);
});
}
@Nested
class WithAdminPermissions {
@@ -180,7 +190,10 @@ class DefaultPluginManagerTest {
manager.install("scm-review-plugin", false);
verify(installer).install(review);
ArgumentCaptor<AvailablePlugin> captor = ArgumentCaptor.forClass(AvailablePlugin.class);
verify(installer).install(captor.capture());
assertThat(captor.getValue().getDescriptor().getInformation().getName()).isEqualTo("scm-review-plugin");
}
@Test
@@ -230,6 +243,66 @@ class DefaultPluginManagerTest {
verify(eventBus).post(any(RestartEvent.class));
}
@Test
void shouldNotSendRestartEventIfNoPluginWasInstalled() {
InstalledPlugin gitInstalled = createInstalled("scm-git-plugin");
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(gitInstalled));
manager.install("scm-git-plugin", true);
verify(eventBus, never()).post(any());
}
@Test
void shouldNotInstallAlreadyPendingPlugins() {
AvailablePlugin review = createAvailable("scm-review-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review));
manager.install("scm-review-plugin", false);
manager.install("scm-review-plugin", false);
// only one interaction
verify(installer).install(any());
}
@Test
void shouldSendRestartEvent() {
AvailablePlugin review = createAvailable("scm-review-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review));
manager.install("scm-review-plugin", false);
manager.installPendingAndRestart();
verify(eventBus).post(any(RestartEvent.class));
}
@Test
void shouldNotSendRestartEventWithoutPendingPlugins() {
manager.installPendingAndRestart();
verify(eventBus, never()).post(any());
}
@Test
void shouldReturnSingleAvailableAsPending() {
AvailablePlugin review = createAvailable("scm-review-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review));
manager.install("scm-review-plugin", false);
Optional<AvailablePlugin> available = manager.getAvailable("scm-review-plugin");
assertThat(available.get().isPending()).isTrue();
}
@Test
void shouldReturnAvailableAsPending() {
AvailablePlugin review = createAvailable("scm-review-plugin");
when(center.getAvailable()).thenReturn(ImmutableSet.of(review));
manager.install("scm-review-plugin", false);
List<AvailablePlugin> available = manager.getAvailable();
assertThat(available.get(0).isPending()).isTrue();
}
}
@Nested
@@ -275,6 +348,11 @@ class DefaultPluginManagerTest {
assertThrows(AuthorizationException.class, () -> manager.install("test", false));
}
@Test
void shouldThrowAuthorizationExceptionsForInstallPendingAndRestart() {
assertThrows(AuthorizationException.class, () -> manager.installPendingAndRestart());
}
}
private AvailablePlugin createAvailable(String name) {
@@ -296,9 +374,9 @@ class DefaultPluginManagerTest {
}
private AvailablePlugin createAvailable(PluginInformation information) {
AvailablePlugin plugin = mock(AvailablePlugin.class, Answers.RETURNS_DEEP_STUBS);
returnInformation(plugin, information);
return plugin;
AvailablePluginDescriptor descriptor = mock(AvailablePluginDescriptor.class);
lenient().when(descriptor.getInformation()).thenReturn(information);
return new AvailablePlugin(descriptor);
}
private void returnInformation(Plugin mockedPlugin, PluginInformation information) {

View File

@@ -63,7 +63,8 @@ class PluginInstallerTest {
PendingPluginInstallation pending = installer.install(gitPlugin);
assertThat(pending).isNotNull();
assertThat(pending.getPlugin()).isSameAs(gitPlugin);
assertThat(pending.getPlugin().getDescriptor()).isEqualTo(gitPlugin.getDescriptor());
assertThat(pending.getPlugin().isPending()).isTrue();
}
private void mockContent(String content) throws IOException {