2020-03-23 15:35:58 +01:00
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2020-03-25 15:31:20 +01:00
|
|
|
|
2019-08-20 12:29:59 +02:00
|
|
|
package sonia.scm.plugin;
|
|
|
|
|
|
|
|
|
|
import com.google.common.collect.ImmutableList;
|
|
|
|
|
import com.google.common.collect.ImmutableSet;
|
2019-08-21 09:25:44 +02:00
|
|
|
import org.apache.shiro.authz.AuthorizationException;
|
|
|
|
|
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.Nested;
|
2019-08-20 12:29:59 +02:00
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
|
import org.junit.jupiter.api.extension.ExtendWith;
|
2019-09-16 13:22:26 +02:00
|
|
|
import org.junitpioneer.jupiter.TempDirectory;
|
2019-08-21 12:49:15 +02:00
|
|
|
import org.mockito.ArgumentCaptor;
|
2020-02-12 14:45:13 +01:00
|
|
|
import org.mockito.InjectMocks;
|
2019-08-20 12:29:59 +02:00
|
|
|
import org.mockito.Mock;
|
|
|
|
|
import org.mockito.junit.jupiter.MockitoExtension;
|
2019-08-21 09:25:44 +02:00
|
|
|
import sonia.scm.NotFoundException;
|
2019-09-16 13:22:26 +02:00
|
|
|
import sonia.scm.ScmConstraintViolationException;
|
2020-02-12 14:45:13 +01:00
|
|
|
import sonia.scm.lifecycle.Restarter;
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-09-26 17:50:54 +02:00
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.nio.file.Files;
|
2019-09-16 13:22:26 +02:00
|
|
|
import java.nio.file.Path;
|
2019-08-20 12:29:59 +02:00
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Optional;
|
|
|
|
|
|
2019-09-16 17:50:05 +02:00
|
|
|
import static java.util.Arrays.asList;
|
2019-09-16 13:22:26 +02:00
|
|
|
import static java.util.Collections.singleton;
|
|
|
|
|
import static java.util.Collections.singletonList;
|
2019-08-20 12:29:59 +02:00
|
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
2019-08-21 08:42:57 +02:00
|
|
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
2019-09-16 13:22:26 +02:00
|
|
|
import static org.mockito.Mockito.any;
|
2019-09-27 11:40:06 +02:00
|
|
|
import static org.mockito.Mockito.doNothing;
|
2019-09-16 13:22:26 +02:00
|
|
|
import static org.mockito.Mockito.doReturn;
|
|
|
|
|
import static org.mockito.Mockito.doThrow;
|
|
|
|
|
import static org.mockito.Mockito.lenient;
|
|
|
|
|
import static org.mockito.Mockito.mock;
|
|
|
|
|
import static org.mockito.Mockito.never;
|
2019-09-30 10:48:26 +02:00
|
|
|
import static org.mockito.Mockito.times;
|
2019-09-16 13:22:26 +02:00
|
|
|
import static org.mockito.Mockito.verify;
|
|
|
|
|
import static org.mockito.Mockito.when;
|
2019-09-16 09:55:38 +02:00
|
|
|
import static sonia.scm.plugin.PluginTestHelper.createAvailable;
|
|
|
|
|
import static sonia.scm.plugin.PluginTestHelper.createInstalled;
|
2019-08-20 12:29:59 +02:00
|
|
|
|
|
|
|
|
@ExtendWith(MockitoExtension.class)
|
2019-09-16 13:22:26 +02:00
|
|
|
@ExtendWith(TempDirectory.class)
|
2019-08-20 12:29:59 +02:00
|
|
|
class DefaultPluginManagerTest {
|
|
|
|
|
|
|
|
|
|
@Mock
|
|
|
|
|
private PluginLoader loader;
|
|
|
|
|
|
|
|
|
|
@Mock
|
|
|
|
|
private PluginCenter center;
|
|
|
|
|
|
2019-08-20 14:43:48 +02:00
|
|
|
@Mock
|
|
|
|
|
private PluginInstaller installer;
|
|
|
|
|
|
2020-02-12 14:45:13 +01:00
|
|
|
@Mock
|
|
|
|
|
private Restarter restarter;
|
|
|
|
|
|
|
|
|
|
@InjectMocks
|
2019-08-20 12:29:59 +02:00
|
|
|
private DefaultPluginManager manager;
|
|
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
@Mock
|
|
|
|
|
private Subject subject;
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 12:49:15 +02:00
|
|
|
@BeforeEach
|
|
|
|
|
void mockInstaller() {
|
|
|
|
|
lenient().when(installer.install(any())).then(ic -> {
|
|
|
|
|
AvailablePlugin plugin = ic.getArgument(0);
|
|
|
|
|
return new PendingPluginInstallation(plugin.install(), null);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
@Nested
|
|
|
|
|
class WithAdminPermissions {
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
@BeforeEach
|
|
|
|
|
void setUpSubject() {
|
|
|
|
|
ThreadContext.bind(subject);
|
|
|
|
|
}
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
@AfterEach
|
|
|
|
|
void clearThreadContext() {
|
|
|
|
|
ThreadContext.unbindSubject();
|
|
|
|
|
}
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
@Test
|
|
|
|
|
void shouldReturnInstalledPlugins() {
|
|
|
|
|
InstalledPlugin review = createInstalled("scm-review-plugin");
|
|
|
|
|
InstalledPlugin git = createInstalled("scm-git-plugin");
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(review, git));
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
List<InstalledPlugin> installed = manager.getInstalled();
|
|
|
|
|
assertThat(installed).containsOnly(review, git);
|
|
|
|
|
}
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
@Test
|
|
|
|
|
void shouldReturnReviewPlugin() {
|
|
|
|
|
InstalledPlugin review = createInstalled("scm-review-plugin");
|
|
|
|
|
InstalledPlugin git = createInstalled("scm-git-plugin");
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(review, git));
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
Optional<InstalledPlugin> plugin = manager.getInstalled("scm-review-plugin");
|
|
|
|
|
assertThat(plugin).contains(review);
|
|
|
|
|
}
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
@Test
|
|
|
|
|
void shouldReturnEmptyForNonInstalledPlugin() {
|
|
|
|
|
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of());
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
Optional<InstalledPlugin> plugin = manager.getInstalled("scm-review-plugin");
|
|
|
|
|
assertThat(plugin).isEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void shouldReturnAvailablePlugins() {
|
|
|
|
|
AvailablePlugin review = createAvailable("scm-review-plugin");
|
|
|
|
|
AvailablePlugin git = createAvailable("scm-git-plugin");
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
when(center.getAvailable()).thenReturn(ImmutableSet.of(review, git));
|
|
|
|
|
|
|
|
|
|
List<AvailablePlugin> available = manager.getAvailable();
|
|
|
|
|
assertThat(available).containsOnly(review, git);
|
|
|
|
|
}
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
@Test
|
|
|
|
|
void shouldFilterOutAllInstalled() {
|
|
|
|
|
InstalledPlugin installedGit = createInstalled("scm-git-plugin");
|
|
|
|
|
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(installedGit));
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
AvailablePlugin review = createAvailable("scm-review-plugin");
|
|
|
|
|
AvailablePlugin git = createAvailable("scm-git-plugin");
|
|
|
|
|
when(center.getAvailable()).thenReturn(ImmutableSet.of(review, git));
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
List<AvailablePlugin> available = manager.getAvailable();
|
|
|
|
|
assertThat(available).containsOnly(review);
|
|
|
|
|
}
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
@Test
|
|
|
|
|
void shouldReturnAvailable() {
|
|
|
|
|
AvailablePlugin review = createAvailable("scm-review-plugin");
|
|
|
|
|
AvailablePlugin git = createAvailable("scm-git-plugin");
|
|
|
|
|
when(center.getAvailable()).thenReturn(ImmutableSet.of(review, git));
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
Optional<AvailablePlugin> available = manager.getAvailable("scm-git-plugin");
|
|
|
|
|
assertThat(available).contains(git);
|
|
|
|
|
}
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
@Test
|
|
|
|
|
void shouldReturnEmptyForNonExistingAvailable() {
|
|
|
|
|
AvailablePlugin review = createAvailable("scm-review-plugin");
|
|
|
|
|
when(center.getAvailable()).thenReturn(ImmutableSet.of(review));
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
Optional<AvailablePlugin> available = manager.getAvailable("scm-git-plugin");
|
|
|
|
|
assertThat(available).isEmpty();
|
|
|
|
|
}
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
@Test
|
|
|
|
|
void shouldReturnEmptyForInstalledPlugin() {
|
|
|
|
|
InstalledPlugin installedGit = createInstalled("scm-git-plugin");
|
|
|
|
|
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(installedGit));
|
2019-08-20 12:29:59 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
AvailablePlugin git = createAvailable("scm-git-plugin");
|
|
|
|
|
when(center.getAvailable()).thenReturn(ImmutableSet.of(git));
|
2019-08-20 14:43:48 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
Optional<AvailablePlugin> available = manager.getAvailable("scm-git-plugin");
|
|
|
|
|
assertThat(available).isEmpty();
|
|
|
|
|
}
|
2019-08-20 14:43:48 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
@Test
|
|
|
|
|
void shouldInstallThePlugin() {
|
|
|
|
|
AvailablePlugin git = createAvailable("scm-git-plugin");
|
|
|
|
|
when(center.getAvailable()).thenReturn(ImmutableSet.of(git));
|
2019-08-20 14:43:48 +02:00
|
|
|
|
2019-08-21 11:22:49 +02:00
|
|
|
manager.install("scm-git-plugin", false);
|
2019-08-20 14:43:48 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
verify(installer).install(git);
|
2020-02-12 14:45:13 +01:00
|
|
|
verify(restarter, never()).restart(any(), any());
|
2019-08-21 09:25:44 +02:00
|
|
|
}
|
2019-08-20 14:43:48 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
@Test
|
|
|
|
|
void shouldInstallDependingPlugins() {
|
|
|
|
|
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));
|
|
|
|
|
|
2019-08-21 11:22:49 +02:00
|
|
|
manager.install("scm-review-plugin", false);
|
2019-08-21 09:25:44 +02:00
|
|
|
|
|
|
|
|
verify(installer).install(mail);
|
|
|
|
|
verify(installer).install(review);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void shouldNotInstallAlreadyInstalledDependencies() {
|
|
|
|
|
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));
|
2019-08-20 14:43:48 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
InstalledPlugin installedMail = createInstalled("scm-mail-plugin");
|
|
|
|
|
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(installedMail));
|
2019-08-20 14:43:48 +02:00
|
|
|
|
2019-08-21 11:22:49 +02:00
|
|
|
manager.install("scm-review-plugin", false);
|
2019-08-20 14:43:48 +02:00
|
|
|
|
2019-08-21 12:49:15 +02:00
|
|
|
ArgumentCaptor<AvailablePlugin> captor = ArgumentCaptor.forClass(AvailablePlugin.class);
|
|
|
|
|
verify(installer).install(captor.capture());
|
|
|
|
|
|
|
|
|
|
assertThat(captor.getValue().getDescriptor().getInformation().getName()).isEqualTo("scm-review-plugin");
|
2019-08-21 09:25:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void shouldRollbackOnFailedInstallation() {
|
|
|
|
|
AvailablePlugin review = createAvailable("scm-review-plugin");
|
|
|
|
|
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"));
|
|
|
|
|
AvailablePlugin notification = createAvailable("scm-notification-plugin");
|
|
|
|
|
when(center.getAvailable()).thenReturn(ImmutableSet.of(review, mail, notification));
|
|
|
|
|
|
|
|
|
|
PendingPluginInstallation pendingNotification = mock(PendingPluginInstallation.class);
|
|
|
|
|
doReturn(pendingNotification).when(installer).install(notification);
|
|
|
|
|
|
|
|
|
|
PendingPluginInstallation pendingMail = mock(PendingPluginInstallation.class);
|
|
|
|
|
doReturn(pendingMail).when(installer).install(mail);
|
|
|
|
|
|
2020-03-25 15:31:20 +01:00
|
|
|
doThrow(new PluginChecksumMismatchException(mail, "1", "2")).when(installer).install(review);
|
2019-08-21 09:25:44 +02:00
|
|
|
|
2019-08-21 11:22:49 +02:00
|
|
|
assertThrows(PluginInstallException.class, () -> manager.install("scm-review-plugin", false));
|
2019-08-21 09:25:44 +02:00
|
|
|
|
|
|
|
|
verify(pendingNotification).cancel();
|
|
|
|
|
verify(pendingMail).cancel();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void shouldInstallNothingIfOneOfTheDependenciesIsNotAvailable() {
|
|
|
|
|
AvailablePlugin review = createAvailable("scm-review-plugin");
|
|
|
|
|
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));
|
|
|
|
|
|
2019-08-21 11:22:49 +02:00
|
|
|
assertThrows(NotFoundException.class, () -> manager.install("scm-review-plugin", false));
|
2019-08-21 09:25:44 +02:00
|
|
|
|
|
|
|
|
verify(installer, never()).install(any());
|
|
|
|
|
}
|
2019-08-20 14:43:48 +02:00
|
|
|
|
2019-08-21 11:22:49 +02:00
|
|
|
@Test
|
|
|
|
|
void shouldSendRestartEventAfterInstallation() {
|
|
|
|
|
AvailablePlugin git = createAvailable("scm-git-plugin");
|
|
|
|
|
when(center.getAvailable()).thenReturn(ImmutableSet.of(git));
|
|
|
|
|
|
|
|
|
|
manager.install("scm-git-plugin", true);
|
|
|
|
|
|
|
|
|
|
verify(installer).install(git);
|
2020-02-12 14:45:13 +01:00
|
|
|
verify(restarter).restart(any(), any());
|
2019-08-21 11:22:49 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-21 12:49:15 +02:00
|
|
|
@Test
|
|
|
|
|
void shouldNotSendRestartEventIfNoPluginWasInstalled() {
|
|
|
|
|
InstalledPlugin gitInstalled = createInstalled("scm-git-plugin");
|
|
|
|
|
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(gitInstalled));
|
|
|
|
|
|
|
|
|
|
manager.install("scm-git-plugin", true);
|
2020-02-12 14:45:13 +01:00
|
|
|
verify(restarter, never()).restart(any(), any());
|
2019-08-21 12:49:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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);
|
2019-09-17 10:36:52 +02:00
|
|
|
manager.executePendingAndRestart();
|
2019-08-21 12:49:15 +02:00
|
|
|
|
2020-02-12 14:45:13 +01:00
|
|
|
verify(restarter).restart(any(), any());
|
2019-08-21 12:49:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void shouldNotSendRestartEventWithoutPendingPlugins() {
|
2019-09-17 10:36:52 +02:00
|
|
|
manager.executePendingAndRestart();
|
2019-08-21 12:49:15 +02:00
|
|
|
|
2020-02-12 14:45:13 +01:00
|
|
|
verify(restarter, never()).restart(any(), any());
|
2019-08-21 12:49:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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();
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-16 13:22:26 +02:00
|
|
|
@Test
|
|
|
|
|
void shouldThrowExceptionWhenUninstallingUnknownPlugin() {
|
|
|
|
|
assertThrows(NotFoundException.class, () -> manager.uninstall("no-such-plugin", false));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void shouldUseDependencyTrackerForUninstall() {
|
|
|
|
|
InstalledPlugin mailPlugin = createInstalled("scm-mail-plugin");
|
|
|
|
|
InstalledPlugin reviewPlugin = createInstalled("scm-review-plugin");
|
|
|
|
|
when(reviewPlugin.getDescriptor().getDependencies()).thenReturn(singleton("scm-mail-plugin"));
|
|
|
|
|
|
|
|
|
|
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(mailPlugin, reviewPlugin));
|
2019-09-16 17:50:05 +02:00
|
|
|
manager.computeInstallationDependencies();
|
2019-09-16 13:22:26 +02:00
|
|
|
|
|
|
|
|
assertThrows(ScmConstraintViolationException.class, () -> manager.uninstall("scm-mail-plugin", false));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void shouldCreateUninstallFile(@TempDirectory.TempDir Path temp) {
|
|
|
|
|
InstalledPlugin mailPlugin = createInstalled("scm-mail-plugin");
|
|
|
|
|
when(mailPlugin.getDirectory()).thenReturn(temp);
|
|
|
|
|
|
|
|
|
|
when(loader.getInstalledPlugins()).thenReturn(singletonList(mailPlugin));
|
|
|
|
|
|
|
|
|
|
manager.uninstall("scm-mail-plugin", false);
|
|
|
|
|
|
|
|
|
|
assertThat(temp.resolve("uninstall")).exists();
|
|
|
|
|
}
|
2019-09-16 14:12:49 +02:00
|
|
|
|
2019-09-16 14:32:14 +02:00
|
|
|
@Test
|
|
|
|
|
void shouldMarkPluginForUninstall(@TempDirectory.TempDir Path temp) {
|
|
|
|
|
InstalledPlugin mailPlugin = createInstalled("scm-mail-plugin");
|
|
|
|
|
when(mailPlugin.getDirectory()).thenReturn(temp);
|
|
|
|
|
|
|
|
|
|
when(loader.getInstalledPlugins()).thenReturn(singletonList(mailPlugin));
|
|
|
|
|
|
|
|
|
|
manager.uninstall("scm-mail-plugin", false);
|
|
|
|
|
|
|
|
|
|
verify(mailPlugin).setMarkedForUninstall(true);
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-26 16:51:26 +02:00
|
|
|
@Test
|
|
|
|
|
void shouldNotChangeStateWhenUninstallFileCouldNotBeCreated() {
|
|
|
|
|
InstalledPlugin mailPlugin = createInstalled("scm-mail-plugin");
|
|
|
|
|
InstalledPlugin reviewPlugin = createInstalled("scm-review-plugin");
|
|
|
|
|
when(reviewPlugin.getDescriptor().getDependencies()).thenReturn(singleton("scm-mail-plugin"));
|
|
|
|
|
|
|
|
|
|
when(reviewPlugin.getDirectory()).thenThrow(new PluginException("when the file could not be written an exception like this is thrown"));
|
|
|
|
|
|
|
|
|
|
when(loader.getInstalledPlugins()).thenReturn(asList(mailPlugin, reviewPlugin));
|
|
|
|
|
|
|
|
|
|
manager.computeInstallationDependencies();
|
|
|
|
|
|
|
|
|
|
assertThrows(PluginException.class, () -> manager.uninstall("scm-review-plugin", false));
|
|
|
|
|
|
|
|
|
|
verify(mailPlugin, never()).setMarkedForUninstall(true);
|
|
|
|
|
assertThrows(ScmConstraintViolationException.class, () -> manager.uninstall("scm-mail-plugin", false));
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-16 14:12:49 +02:00
|
|
|
@Test
|
|
|
|
|
void shouldThrowExceptionWhenUninstallingCorePlugin(@TempDirectory.TempDir Path temp) {
|
|
|
|
|
InstalledPlugin mailPlugin = createInstalled("scm-mail-plugin");
|
|
|
|
|
when(mailPlugin.getDirectory()).thenReturn(temp);
|
|
|
|
|
when(mailPlugin.isCore()).thenReturn(true);
|
|
|
|
|
|
|
|
|
|
when(loader.getInstalledPlugins()).thenReturn(singletonList(mailPlugin));
|
|
|
|
|
|
|
|
|
|
assertThrows(ScmConstraintViolationException.class, () -> manager.uninstall("scm-mail-plugin", false));
|
|
|
|
|
|
|
|
|
|
assertThat(temp.resolve("uninstall")).doesNotExist();
|
|
|
|
|
}
|
2019-09-16 17:50:05 +02:00
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void shouldMarkUninstallablePlugins() {
|
|
|
|
|
InstalledPlugin mailPlugin = createInstalled("scm-mail-plugin");
|
|
|
|
|
InstalledPlugin reviewPlugin = createInstalled("scm-review-plugin");
|
|
|
|
|
when(reviewPlugin.getDescriptor().getDependencies()).thenReturn(singleton("scm-mail-plugin"));
|
|
|
|
|
|
|
|
|
|
when(loader.getInstalledPlugins()).thenReturn(asList(mailPlugin, reviewPlugin));
|
|
|
|
|
|
|
|
|
|
manager.computeInstallationDependencies();
|
|
|
|
|
|
|
|
|
|
verify(reviewPlugin).setUninstallable(true);
|
|
|
|
|
verify(mailPlugin).setUninstallable(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void shouldUpdateMayUninstallFlagAfterDependencyIsUninstalled() {
|
|
|
|
|
InstalledPlugin mailPlugin = createInstalled("scm-mail-plugin");
|
|
|
|
|
InstalledPlugin reviewPlugin = createInstalled("scm-review-plugin");
|
|
|
|
|
when(reviewPlugin.getDescriptor().getDependencies()).thenReturn(singleton("scm-mail-plugin"));
|
|
|
|
|
|
|
|
|
|
when(loader.getInstalledPlugins()).thenReturn(asList(mailPlugin, reviewPlugin));
|
|
|
|
|
|
|
|
|
|
manager.computeInstallationDependencies();
|
|
|
|
|
|
|
|
|
|
manager.uninstall("scm-review-plugin", false);
|
|
|
|
|
|
|
|
|
|
verify(mailPlugin).setUninstallable(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void shouldUpdateMayUninstallFlagAfterDependencyIsInstalled() {
|
|
|
|
|
InstalledPlugin mailPlugin = createInstalled("scm-mail-plugin");
|
|
|
|
|
AvailablePlugin reviewPlugin = createAvailable("scm-review-plugin");
|
|
|
|
|
when(reviewPlugin.getDescriptor().getDependencies()).thenReturn(singleton("scm-mail-plugin"));
|
|
|
|
|
|
|
|
|
|
when(loader.getInstalledPlugins()).thenReturn(singletonList(mailPlugin));
|
|
|
|
|
when(center.getAvailable()).thenReturn(singleton(reviewPlugin));
|
|
|
|
|
|
|
|
|
|
manager.computeInstallationDependencies();
|
|
|
|
|
|
|
|
|
|
manager.install("scm-review-plugin", false);
|
|
|
|
|
|
|
|
|
|
verify(mailPlugin).setUninstallable(false);
|
|
|
|
|
}
|
2019-09-18 08:35:59 +02:00
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void shouldRestartWithUninstallOnly() {
|
|
|
|
|
InstalledPlugin mailPlugin = createInstalled("scm-mail-plugin");
|
|
|
|
|
when(mailPlugin.isMarkedForUninstall()).thenReturn(true);
|
|
|
|
|
|
|
|
|
|
when(loader.getInstalledPlugins()).thenReturn(singletonList(mailPlugin));
|
|
|
|
|
|
|
|
|
|
manager.executePendingAndRestart();
|
|
|
|
|
|
2020-02-12 14:45:13 +01:00
|
|
|
verify(restarter).restart(any(), any());
|
2019-09-18 08:35:59 +02:00
|
|
|
}
|
2019-09-26 17:50:54 +02:00
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void shouldUndoPendingInstallations(@TempDirectory.TempDir Path temp) throws IOException {
|
|
|
|
|
InstalledPlugin mailPlugin = createInstalled("scm-ssh-plugin");
|
|
|
|
|
Path mailPluginPath = temp.resolve("scm-mail-plugin");
|
|
|
|
|
Files.createDirectories(mailPluginPath);
|
|
|
|
|
when(mailPlugin.getDirectory()).thenReturn(mailPluginPath);
|
|
|
|
|
when(loader.getInstalledPlugins()).thenReturn(singletonList(mailPlugin));
|
2019-09-27 11:40:06 +02:00
|
|
|
ArgumentCaptor<Boolean> uninstallCaptor = ArgumentCaptor.forClass(Boolean.class);
|
|
|
|
|
doNothing().when(mailPlugin).setMarkedForUninstall(uninstallCaptor.capture());
|
2019-09-26 17:50:54 +02:00
|
|
|
|
|
|
|
|
AvailablePlugin git = createAvailable("scm-git-plugin");
|
|
|
|
|
when(center.getAvailable()).thenReturn(ImmutableSet.of(git));
|
|
|
|
|
PendingPluginInstallation gitPendingPluginInformation = mock(PendingPluginInstallation.class);
|
|
|
|
|
when(installer.install(git)).thenReturn(gitPendingPluginInformation);
|
|
|
|
|
|
|
|
|
|
manager.install("scm-git-plugin", false);
|
|
|
|
|
manager.uninstall("scm-ssh-plugin", false);
|
|
|
|
|
|
2019-09-27 11:46:14 +02:00
|
|
|
manager.cancelPending();
|
2019-09-26 17:50:54 +02:00
|
|
|
|
|
|
|
|
assertThat(mailPluginPath.resolve("uninstall")).doesNotExist();
|
|
|
|
|
verify(gitPendingPluginInformation).cancel();
|
2019-09-27 11:40:06 +02:00
|
|
|
Boolean lasUninstallMarkerSet = uninstallCaptor.getAllValues().get(uninstallCaptor.getAllValues().size() - 1);
|
|
|
|
|
assertThat(lasUninstallMarkerSet).isFalse();
|
2019-09-30 10:48:26 +02:00
|
|
|
|
|
|
|
|
Files.createFile(mailPluginPath.resolve("uninstall"));
|
|
|
|
|
|
|
|
|
|
manager.cancelPending();
|
|
|
|
|
verify(gitPendingPluginInformation, times(1)).cancel();
|
|
|
|
|
assertThat(mailPluginPath.resolve("uninstall")).exists();
|
2019-09-26 17:50:54 +02:00
|
|
|
}
|
2019-09-27 15:30:21 +02:00
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void shouldUpdateAllPlugins() {
|
|
|
|
|
InstalledPlugin mailPlugin = createInstalled("scm-mail-plugin");
|
|
|
|
|
InstalledPlugin reviewPlugin = createInstalled("scm-review-plugin");
|
|
|
|
|
|
|
|
|
|
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(mailPlugin, reviewPlugin));
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
|
2019-09-28 11:44:39 +02:00
|
|
|
manager.updateAll();
|
2019-09-27 15:30:21 +02:00
|
|
|
|
|
|
|
|
verify(installer).install(newMailPlugin);
|
|
|
|
|
verify(installer).install(newReviewPlugin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void shouldNotUpdateToOldPluginVersions() {
|
|
|
|
|
InstalledPlugin scriptPlugin = createInstalled("scm-script-plugin");
|
|
|
|
|
|
|
|
|
|
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(scriptPlugin));
|
|
|
|
|
AvailablePlugin oldScriptPlugin = createAvailable("scm-script-plugin", "0.9");
|
|
|
|
|
|
|
|
|
|
when(center.getAvailable()).thenReturn(ImmutableSet.of(oldScriptPlugin));
|
|
|
|
|
|
2019-09-28 11:44:39 +02:00
|
|
|
manager.updateAll();
|
2019-09-27 15:30:21 +02:00
|
|
|
|
|
|
|
|
verify(installer, never()).install(oldScriptPlugin);
|
|
|
|
|
}
|
2019-08-20 14:43:48 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
@Nested
|
|
|
|
|
class WithoutReadPermissions {
|
|
|
|
|
|
|
|
|
|
@BeforeEach
|
|
|
|
|
void setUpSubject() {
|
|
|
|
|
ThreadContext.bind(subject);
|
|
|
|
|
doThrow(AuthorizationException.class).when(subject).checkPermission("plugin:read");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@AfterEach
|
|
|
|
|
void clearThreadContext() {
|
|
|
|
|
ThreadContext.unbindSubject();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void shouldThrowAuthorizationExceptionsForReadMethods() {
|
|
|
|
|
assertThrows(AuthorizationException.class, () -> manager.getInstalled());
|
|
|
|
|
assertThrows(AuthorizationException.class, () -> manager.getInstalled("test"));
|
|
|
|
|
assertThrows(AuthorizationException.class, () -> manager.getAvailable());
|
|
|
|
|
assertThrows(AuthorizationException.class, () -> manager.getAvailable("test"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2019-08-21 08:42:57 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
@Nested
|
|
|
|
|
class WithoutManagePermissions {
|
2019-08-21 08:42:57 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
@BeforeEach
|
|
|
|
|
void setUpSubject() {
|
|
|
|
|
ThreadContext.bind(subject);
|
|
|
|
|
doThrow(AuthorizationException.class).when(subject).checkPermission("plugin:manage");
|
|
|
|
|
}
|
2019-08-21 08:42:57 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
@AfterEach
|
|
|
|
|
void clearThreadContext() {
|
|
|
|
|
ThreadContext.unbindSubject();
|
|
|
|
|
}
|
2019-08-21 08:42:57 +02:00
|
|
|
|
2019-08-21 09:25:44 +02:00
|
|
|
@Test
|
|
|
|
|
void shouldThrowAuthorizationExceptionsForInstallMethod() {
|
2019-08-21 11:22:49 +02:00
|
|
|
assertThrows(AuthorizationException.class, () -> manager.install("test", false));
|
2019-08-21 09:25:44 +02:00
|
|
|
}
|
2019-08-21 08:42:57 +02:00
|
|
|
|
2019-09-16 13:22:26 +02:00
|
|
|
@Test
|
|
|
|
|
void shouldThrowAuthorizationExceptionsForUninstallMethod() {
|
|
|
|
|
assertThrows(AuthorizationException.class, () -> manager.uninstall("test", false));
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-21 12:49:15 +02:00
|
|
|
@Test
|
2019-09-17 10:36:52 +02:00
|
|
|
void shouldThrowAuthorizationExceptionsForExecutePendingAndRestart() {
|
|
|
|
|
assertThrows(AuthorizationException.class, () -> manager.executePendingAndRestart());
|
2019-08-21 12:49:15 +02:00
|
|
|
}
|
|
|
|
|
|
2019-09-27 11:46:14 +02:00
|
|
|
@Test
|
|
|
|
|
void shouldThrowAuthorizationExceptionsForCancelPending() {
|
|
|
|
|
assertThrows(AuthorizationException.class, () -> manager.cancelPending());
|
|
|
|
|
}
|
2019-09-27 15:30:21 +02:00
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
void shouldThrowAuthorizationExceptionsForUpdateAll() {
|
2019-09-28 11:44:39 +02:00
|
|
|
assertThrows(AuthorizationException.class, () -> manager.updateAll());
|
2019-09-27 15:30:21 +02:00
|
|
|
}
|
2019-08-21 08:42:57 +02:00
|
|
|
}
|
2019-08-20 12:29:59 +02:00
|
|
|
}
|