mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-13 08:55:44 +01:00
rename package sonia.scm.boot to sonia.scm.lifecycle
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
package sonia.scm.lifecycle;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
|
||||
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventorFactory;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ClassLoaderLifeCycleTest {
|
||||
|
||||
@Mock
|
||||
private ClassLoaderLeakPreventorFactory classLoaderLeakPreventorFactory;
|
||||
|
||||
@Mock
|
||||
private ClassLoaderLeakPreventor classLoaderLeakPreventor;
|
||||
|
||||
@Test
|
||||
void shouldThrowIllegalStateExceptionWithoutInit() {
|
||||
ClassLoaderLifeCycle lifeCycle = ClassLoaderLifeCycle.create();
|
||||
assertThrows(IllegalStateException.class, lifeCycle::getBootstrapClassLoader);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowIllegalStateExceptionAfterShutdown() {
|
||||
ClassLoaderLifeCycle lifeCycle = createMockedLifeCycle();
|
||||
lifeCycle.init();
|
||||
|
||||
lifeCycle.shutdown();
|
||||
assertThrows(IllegalStateException.class, lifeCycle::getBootstrapClassLoader);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateBootstrapClassLoaderOnInit() {
|
||||
ClassLoaderLifeCycle lifeCycle = ClassLoaderLifeCycle.create();
|
||||
lifeCycle.init();
|
||||
|
||||
assertThat(lifeCycle.getBootstrapClassLoader()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCallTheLeakPreventor() {
|
||||
ClassLoaderLifeCycle lifeCycle = createMockedLifeCycle();
|
||||
|
||||
lifeCycle.init();
|
||||
verify(classLoaderLeakPreventor, times(2)).runPreClassLoaderInitiators();
|
||||
|
||||
lifeCycle.createChildFirstPluginClassLoader(new URL[0], null, "a");
|
||||
lifeCycle.createPluginClassLoader(new URL[0], null, "b");
|
||||
verify(classLoaderLeakPreventor, times(4)).runPreClassLoaderInitiators();
|
||||
|
||||
lifeCycle.shutdown();
|
||||
verify(classLoaderLeakPreventor, times(4)).runCleanUps();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCloseCloseableClassLoaders() throws IOException {
|
||||
// we use URLClassLoader, because we must be sure that the classloader is closable
|
||||
URLClassLoader webappClassLoader = spy(new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()));
|
||||
|
||||
ClassLoaderLifeCycle lifeCycle = createMockedLifeCycle(webappClassLoader);
|
||||
lifeCycle.setClassLoaderAppendListener(c -> spy(c));
|
||||
lifeCycle.init();
|
||||
|
||||
ClassLoader pluginA = lifeCycle.createChildFirstPluginClassLoader(new URL[0], null, "a");
|
||||
ClassLoader pluginB = lifeCycle.createPluginClassLoader(new URL[0], null, "b");
|
||||
|
||||
lifeCycle.shutdown();
|
||||
|
||||
closed(pluginB);
|
||||
closed(pluginA);
|
||||
|
||||
neverClosed(webappClassLoader);
|
||||
}
|
||||
|
||||
private void neverClosed(Object object) throws IOException {
|
||||
Closeable closeable = closeable(object);
|
||||
verify(closeable, never()).close();
|
||||
}
|
||||
|
||||
private void closed(Object object) throws IOException {
|
||||
Closeable closeable = closeable(object);
|
||||
verify(closeable).close();
|
||||
}
|
||||
|
||||
private Closeable closeable(Object object) {
|
||||
assertThat(object).isInstanceOf(Closeable.class);
|
||||
return (Closeable) object;
|
||||
}
|
||||
|
||||
private ClassLoaderLifeCycle createMockedLifeCycle() {
|
||||
return createMockedLifeCycle(Thread.currentThread().getContextClassLoader());
|
||||
}
|
||||
|
||||
private ClassLoaderLifeCycle createMockedLifeCycle(ClassLoader classLoader) {
|
||||
when(classLoaderLeakPreventorFactory.newLeakPreventor(any(ClassLoader.class))).thenReturn(classLoaderLeakPreventor);
|
||||
return new ClassLoaderLifeCycle(classLoader, classLoaderLeakPreventorFactory);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package sonia.scm.lifecycle;
|
||||
|
||||
import com.github.legman.Subscribe;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.event.RecreateEventBusEvent;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class InjectionContextRestartStrategyTest {
|
||||
|
||||
@Mock
|
||||
private RestartStrategy.InjectionContext context;
|
||||
|
||||
private InjectionContextRestartStrategy strategy = new InjectionContextRestartStrategy();
|
||||
|
||||
@BeforeEach
|
||||
void setWaitToZero() {
|
||||
strategy.setWaitInMs(0L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCallDestroyAndInitialize() throws InterruptedException {
|
||||
strategy.restart(context);
|
||||
|
||||
verify(context).destroy();
|
||||
Thread.sleep(50L);
|
||||
verify(context).initialize();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFireRecreateEventBusEvent() {
|
||||
Listener listener = new Listener();
|
||||
ScmEventBus.getInstance().register(listener);
|
||||
|
||||
strategy.restart(context);
|
||||
|
||||
assertThat(listener.event).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRegisterContextAfterRestart() throws InterruptedException {
|
||||
TestingInjectionContext ctx = new TestingInjectionContext();
|
||||
|
||||
strategy.restart(ctx);
|
||||
|
||||
Thread.sleep(50L);
|
||||
ScmEventBus.getInstance().post("hello event");
|
||||
|
||||
assertThat(ctx.event).isEqualTo("hello event");
|
||||
}
|
||||
|
||||
public static class Listener {
|
||||
|
||||
private RecreateEventBusEvent event;
|
||||
|
||||
@Subscribe(async = false)
|
||||
public void setEvent(RecreateEventBusEvent event) {
|
||||
this.event = event;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestingInjectionContext implements RestartStrategy.InjectionContext {
|
||||
|
||||
private volatile String event;
|
||||
|
||||
@Subscribe(async = false)
|
||||
public void setEvent(String event) {
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
package sonia.scm.lifecycle;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.multibindings.Multibinder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.CloseableModule;
|
||||
import sonia.scm.Default;
|
||||
import sonia.scm.EagerSingleton;
|
||||
import sonia.scm.EagerSingletonModule;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import java.io.Closeable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class InjectionLifeCycleTest {
|
||||
|
||||
@Mock
|
||||
private ServletContext servletContext;
|
||||
|
||||
@Test
|
||||
void shouldInitializeEagerSingletons() {
|
||||
Injector injector = initialize(new EagerSingletonModule(), new EagerModule());
|
||||
|
||||
Messenger messenger = injector.getInstance(Messenger.class);
|
||||
assertThat(messenger.receive()).isEqualTo("eager baby!");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotThrowAnExceptionWithoutEagerSingletons() {
|
||||
Injector injector = initialize(new EagerSingletonModule());
|
||||
|
||||
Messenger messenger = injector.getInstance(Messenger.class);
|
||||
assertThat(messenger.receive()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldInitializeServletContextListeners() {
|
||||
Injector injector = initialize(new ServletContextListenerModule());
|
||||
|
||||
Messenger messenger = injector.getInstance(Messenger.class);
|
||||
assertThat(messenger.receive()).isEqualTo("+4+2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCallDestroyOnServletContextListeners() {
|
||||
Injector injector = createInjector(servletContext, new ServletContextListenerModule());
|
||||
|
||||
InjectionLifeCycle lifeCycle = new InjectionLifeCycle(injector);
|
||||
lifeCycle.shutdown();
|
||||
|
||||
Messenger messenger = injector.getInstance(Messenger.class);
|
||||
assertThat(messenger.receive()).isEqualTo("-4-2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCloseInstantiatedCloseables() {
|
||||
Injector injector = createInjector(servletContext, new FortyTwoModule(), new CloseableModule());
|
||||
|
||||
injector.getInstance(Two.class);
|
||||
injector.getInstance(Four.class);
|
||||
|
||||
|
||||
InjectionLifeCycle lifeCycle = new InjectionLifeCycle(injector);
|
||||
lifeCycle.shutdown();
|
||||
|
||||
Messenger messenger = injector.getInstance(Messenger.class);
|
||||
assertThat(messenger.receive()).isEqualTo("42");
|
||||
}
|
||||
|
||||
|
||||
private Injector initialize(Module... modules) {
|
||||
Injector injector = createInjector(servletContext, modules);
|
||||
|
||||
InjectionLifeCycle lifeCycle = new InjectionLifeCycle(injector);
|
||||
lifeCycle.initialize();
|
||||
|
||||
return injector;
|
||||
}
|
||||
|
||||
public static class EagerModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(ImEager.class);
|
||||
}
|
||||
}
|
||||
|
||||
@EagerSingleton
|
||||
public static class ImEager {
|
||||
|
||||
@Inject
|
||||
public ImEager(Messenger messenger) {
|
||||
messenger.send("eager baby!");
|
||||
}
|
||||
}
|
||||
|
||||
public static class FortyTwoModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(Four.class);
|
||||
bind(Two.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Singleton
|
||||
public static class Four implements Closeable {
|
||||
|
||||
private final Messenger messenger;
|
||||
|
||||
@Inject
|
||||
public Four(Messenger messenger) {
|
||||
this.messenger = messenger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
messenger.append("4");
|
||||
}
|
||||
}
|
||||
|
||||
@Singleton
|
||||
public static class Two implements Closeable {
|
||||
|
||||
private final Messenger messenger;
|
||||
|
||||
@Inject
|
||||
public Two(Messenger messenger) {
|
||||
this.messenger = messenger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
messenger.append("2");
|
||||
}
|
||||
}
|
||||
|
||||
static Injector createInjector(ServletContext context, Module... modules) {
|
||||
List<Module> moduleList = new ArrayList<>();
|
||||
moduleList.add(new ServletContextModule(context));
|
||||
moduleList.addAll(Arrays.asList(modules));
|
||||
|
||||
return Guice.createInjector(moduleList);
|
||||
}
|
||||
|
||||
public static class ServletContextModule extends AbstractModule {
|
||||
|
||||
private final ServletContext servletContext;
|
||||
|
||||
ServletContextModule(ServletContext servletContext) {
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(ServletContext.class).annotatedWith(Default.class).toInstance(servletContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ServletContextListenerModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
Multibinder<ServletContextListener> multibinder = Multibinder.newSetBinder(binder(), ServletContextListener.class);
|
||||
multibinder.addBinding().to(AppendingFourServletContextListener.class);
|
||||
multibinder.addBinding().to(AppendingTwoServletContextListener.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static class AppendingFourServletContextListener extends AppendingServletContextListener {
|
||||
|
||||
@Inject
|
||||
public AppendingFourServletContextListener(Messenger messenger) {
|
||||
super(messenger);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSign() {
|
||||
return "4";
|
||||
}
|
||||
}
|
||||
|
||||
public static class AppendingTwoServletContextListener extends AppendingServletContextListener {
|
||||
|
||||
@Inject
|
||||
public AppendingTwoServletContextListener(Messenger messenger) {
|
||||
super(messenger);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSign() {
|
||||
return "2";
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class AppendingServletContextListener implements ServletContextListener {
|
||||
|
||||
private final Messenger messenger;
|
||||
|
||||
@Inject
|
||||
public AppendingServletContextListener(Messenger messenger) {
|
||||
this.messenger = messenger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce) {
|
||||
send("+");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce) {
|
||||
send("-");
|
||||
}
|
||||
|
||||
private void send(String prefix) {
|
||||
messenger.append(prefix + getSign());
|
||||
}
|
||||
|
||||
protected abstract String getSign();
|
||||
}
|
||||
|
||||
@Singleton
|
||||
public static class Messenger {
|
||||
|
||||
private String message;
|
||||
|
||||
void send(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
void append(String messageToAppend) {
|
||||
send(Strings.nullToEmpty(message) + messageToAppend);
|
||||
}
|
||||
|
||||
String receive() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package sonia.scm.lifecycle;
|
||||
|
||||
import com.github.legman.Subscribe;
|
||||
import com.google.common.base.Charsets;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.Stage;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
import sonia.scm.event.ScmTestEventBus;
|
||||
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class RestartServletTest {
|
||||
|
||||
@Mock
|
||||
private HttpServletRequest request;
|
||||
|
||||
@Mock
|
||||
private HttpServletResponse response;
|
||||
|
||||
private RestartServlet restartServlet;
|
||||
|
||||
private EventListener listener;
|
||||
|
||||
private void setUpObjectUnderTest(Stage stage) {
|
||||
listener = new EventListener();
|
||||
ScmEventBus eventBus = ScmTestEventBus.getInstance();
|
||||
eventBus.register(listener);
|
||||
|
||||
restartServlet = new RestartServlet(eventBus, stage);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestart() throws IOException {
|
||||
setUpObjectUnderTest(Stage.DEVELOPMENT);
|
||||
setRequestInputReason("something changed");
|
||||
|
||||
restartServlet.doPost(request, response);
|
||||
|
||||
verify(response).setStatus(HttpServletResponse.SC_ACCEPTED);
|
||||
|
||||
RestartEvent restartEvent = listener.restartEvent;
|
||||
assertThat(restartEvent).isNotNull();
|
||||
assertThat(restartEvent.getCause()).isEqualTo(RestartServlet.class);
|
||||
assertThat(restartEvent.getReason()).isEqualTo("something changed");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestartCalledTwice() throws IOException {
|
||||
setUpObjectUnderTest(Stage.DEVELOPMENT);
|
||||
|
||||
setRequestInputReason("initial change");
|
||||
restartServlet.doPost(request, response);
|
||||
verify(response).setStatus(HttpServletResponse.SC_ACCEPTED);
|
||||
|
||||
setRequestInputReason("changed again");
|
||||
restartServlet.doPost(request, response);
|
||||
verify(response).setStatus(HttpServletResponse.SC_CONFLICT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestartWithInvalidContent() throws IOException {
|
||||
setUpObjectUnderTest(Stage.DEVELOPMENT);
|
||||
|
||||
setRequestInputContent("invalid json");
|
||||
|
||||
restartServlet.doPost(request, response);
|
||||
|
||||
verify(response).setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestartInProductionStage() throws IOException {
|
||||
setUpObjectUnderTest(Stage.PRODUCTION);
|
||||
|
||||
setRequestInputReason("initial change");
|
||||
|
||||
restartServlet.doPost(request, response);
|
||||
|
||||
verify(response).setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
|
||||
}
|
||||
|
||||
private void setRequestInputReason(String message) throws IOException {
|
||||
String content = createReason(message);
|
||||
setRequestInputContent(content);
|
||||
}
|
||||
|
||||
private void setRequestInputContent(String content) throws IOException {
|
||||
InputStream input = createReasonAsInputStream(content);
|
||||
when(request.getInputStream()).thenReturn(createServletInputStream(input));
|
||||
}
|
||||
|
||||
private ServletInputStream createServletInputStream(final InputStream inputStream) {
|
||||
return new ServletInputStream() {
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return inputStream.read();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private InputStream createReasonAsInputStream(String content) {
|
||||
return new ByteArrayInputStream(content.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
private String createReason(String message) {
|
||||
return String.format("{\"message\": \"%s\"}", message);
|
||||
}
|
||||
|
||||
public static class EventListener {
|
||||
|
||||
private RestartEvent restartEvent;
|
||||
|
||||
@Subscribe(async = false)
|
||||
public void store(RestartEvent restartEvent) {
|
||||
this.restartEvent = restartEvent;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package sonia.scm.lifecycle;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Set;
|
||||
import java.util.Vector;
|
||||
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ServletContextCleanerTest {
|
||||
|
||||
@Mock
|
||||
private ServletContext servletContext;
|
||||
|
||||
@Test
|
||||
public void testCleanup() {
|
||||
Set<String> names = ImmutableSet.of(
|
||||
"org.jboss.resteasy.Dispatcher",
|
||||
"resteasy.Deployment",
|
||||
"sonia.scm.Context",
|
||||
"org.eclipse.jetty.HttpServer",
|
||||
"javax.servlet.Context",
|
||||
"org.apache.shiro.SecurityManager"
|
||||
);
|
||||
|
||||
when(servletContext.getAttributeNames()).thenReturn(toEnumeration(names));
|
||||
|
||||
ServletContextCleaner.cleanup(servletContext);
|
||||
|
||||
verify(servletContext).removeAttribute("org.jboss.resteasy.Dispatcher");
|
||||
verify(servletContext).removeAttribute("resteasy.Deployment");
|
||||
verify(servletContext).removeAttribute("sonia.scm.Context");
|
||||
verify(servletContext, never()).removeAttribute("org.eclipse.jetty.HttpServer");
|
||||
verify(servletContext, never()).removeAttribute("javax.servlet.Context");
|
||||
verify(servletContext).removeAttribute("org.apache.shiro.SecurityManager");
|
||||
}
|
||||
|
||||
private <T> Enumeration<T> toEnumeration(Collection<T> collection) {
|
||||
return new Vector<>(collection).elements();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package sonia.scm.lifecycle;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.servlet.GuiceFilter;
|
||||
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.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.Default;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ServletContextModuleTest {
|
||||
|
||||
@Mock
|
||||
private ServletContext servletContext;
|
||||
|
||||
private GuiceFilter guiceFilter;
|
||||
|
||||
@BeforeEach
|
||||
void setUpEnvironment() throws ServletException {
|
||||
guiceFilter = new GuiceFilter();
|
||||
FilterConfig filterConfig = mock(FilterConfig.class);
|
||||
when(filterConfig.getServletContext()).thenReturn(servletContext);
|
||||
|
||||
guiceFilter.init(filterConfig);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDownEnvironment() {
|
||||
guiceFilter.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBeAbleToInjectServletContext() {
|
||||
Injector injector = Guice.createInjector(new ServletContextModule());
|
||||
WebComponent instance = injector.getInstance(WebComponent.class);
|
||||
assertThat(instance.context).isSameAs(servletContext);
|
||||
}
|
||||
|
||||
|
||||
public static class WebComponent {
|
||||
|
||||
private ServletContext context;
|
||||
|
||||
@Inject
|
||||
public WebComponent(@Default ServletContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package sonia.scm.lifecycle;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.shiro.authc.credential.PasswordService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import sonia.scm.security.PermissionAssigner;
|
||||
import sonia.scm.security.PermissionDescriptor;
|
||||
import sonia.scm.user.User;
|
||||
import sonia.scm.user.UserManager;
|
||||
import sonia.scm.user.UserTestData;
|
||||
import sonia.scm.web.security.AdministrationContext;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SetupContextListenerTest {
|
||||
|
||||
@Mock
|
||||
private AdministrationContext administrationContext;
|
||||
|
||||
@InjectMocks
|
||||
private SetupContextListener setupContextListener;
|
||||
|
||||
@Mock
|
||||
private UserManager userManager;
|
||||
|
||||
@Mock
|
||||
private PasswordService passwordService;
|
||||
|
||||
@Mock
|
||||
private PermissionAssigner permissionAssigner;
|
||||
|
||||
@InjectMocks
|
||||
private SetupContextListener.SetupAction setupAction;
|
||||
|
||||
@BeforeEach
|
||||
void setupObjectUnderTest() {
|
||||
doAnswer(ic -> {
|
||||
setupAction.run();
|
||||
return null;
|
||||
}).when(administrationContext).runAsAdmin(SetupContextListener.SetupAction.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateAdminAccountAndAssignPermissions() {
|
||||
when(passwordService.encryptPassword("scmadmin")).thenReturn("secret");
|
||||
|
||||
setupContextListener.contextInitialized(null);
|
||||
|
||||
verifyAdminCreated();
|
||||
verifyAdminPermissionsAssigned();
|
||||
}
|
||||
|
||||
@Test
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
void shouldSkipAdminAccountCreationIfPropertyIsSet() {
|
||||
System.setProperty("sonia.scm.skipAdminCreation", "true");
|
||||
|
||||
try {
|
||||
setupContextListener.contextInitialized(null);
|
||||
|
||||
verify(userManager, never()).create(any());
|
||||
verify(permissionAssigner, never()).setPermissionsForUser(anyString(), any(Collection.class));
|
||||
} finally {
|
||||
System.setProperty("sonia.scm.skipAdminCreation", "");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDoNothingOnSecondStart() {
|
||||
List<User> users = Lists.newArrayList(UserTestData.createTrillian());
|
||||
when(userManager.getAll()).thenReturn(users);
|
||||
|
||||
setupContextListener.contextInitialized(null);
|
||||
|
||||
verify(userManager, never()).create(any(User.class));
|
||||
verify(permissionAssigner, never()).setPermissionsForUser(anyString(), any(Collection.class));
|
||||
}
|
||||
|
||||
private void verifyAdminPermissionsAssigned() {
|
||||
ArgumentCaptor<String> usernameCaptor = ArgumentCaptor.forClass(String.class);
|
||||
ArgumentCaptor<Collection<PermissionDescriptor>> permissionCaptor = ArgumentCaptor.forClass(Collection.class);
|
||||
verify(permissionAssigner).setPermissionsForUser(usernameCaptor.capture(), permissionCaptor.capture());
|
||||
String username = usernameCaptor.getValue();
|
||||
assertThat(username).isEqualTo("scmadmin");
|
||||
PermissionDescriptor descriptor = permissionCaptor.getValue().iterator().next();
|
||||
assertThat(descriptor.getValue()).isEqualTo("*");
|
||||
}
|
||||
|
||||
private void verifyAdminCreated() {
|
||||
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
|
||||
verify(userManager).create(userCaptor.capture());
|
||||
User user = userCaptor.getValue();
|
||||
assertThat(user.getName()).isEqualTo("scmadmin");
|
||||
assertThat(user.getPassword()).isEqualTo("secret");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package sonia.scm.lifecycle;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.template.Template;
|
||||
import sonia.scm.template.TemplateEngine;
|
||||
import sonia.scm.template.TemplateEngineFactory;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SingleViewServletTest {
|
||||
|
||||
@Mock
|
||||
private TemplateEngineFactory templateEngineFactory;
|
||||
|
||||
@Mock
|
||||
private TemplateEngine templateEngine;
|
||||
|
||||
@Mock
|
||||
private Template template;
|
||||
|
||||
@Mock
|
||||
private HttpServletRequest request;
|
||||
|
||||
@Mock
|
||||
private HttpServletResponse response;
|
||||
|
||||
@Mock
|
||||
private PrintWriter writer;
|
||||
|
||||
@Mock
|
||||
private ViewController controller;
|
||||
|
||||
@Test
|
||||
void shouldRenderTheTemplateOnGet() throws IOException {
|
||||
prepareTemplate("/template");
|
||||
doReturn(new View(200, "hello")).when(controller).createView(request);
|
||||
|
||||
new SingleViewServlet(templateEngineFactory, controller).doGet(request, response);
|
||||
|
||||
verifyResponse(200, "hello");
|
||||
}
|
||||
|
||||
private void verifyResponse(int sc, Object model) throws IOException {
|
||||
verify(response).setStatus(sc);
|
||||
verify(response).setContentType("text/html");
|
||||
verify(response).setCharacterEncoding("UTF-8");
|
||||
|
||||
verify(template).execute(writer, model);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRenderTheTemplateOnPost() throws IOException {
|
||||
prepareTemplate("/template");
|
||||
|
||||
doReturn(new View(201, "hello")).when(controller).createView(request);
|
||||
|
||||
new SingleViewServlet(templateEngineFactory, controller).doPost(request, response);
|
||||
|
||||
verifyResponse(201, "hello");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowIllegalStateExceptionOnIOException() throws IOException {
|
||||
doReturn("/template").when(controller).getTemplate();
|
||||
doReturn(templateEngine).when(templateEngineFactory).getEngineByExtension("/template");
|
||||
doThrow(IOException.class).when(templateEngine).getTemplate("/template");
|
||||
|
||||
assertThrows(IllegalStateException.class, () -> new SingleViewServlet(templateEngineFactory, controller));
|
||||
}
|
||||
|
||||
private void prepareTemplate(String templatePath) throws IOException {
|
||||
doReturn(templateEngine).when(templateEngineFactory).getEngineByExtension(templatePath);
|
||||
doReturn(template).when(templateEngine).getTemplate(templatePath);
|
||||
doReturn(templatePath).when(controller).getTemplate();
|
||||
|
||||
doReturn(writer).when(response).getWriter();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package sonia.scm.lifecycle;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.servlet.GuiceFilter;
|
||||
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.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SingleViewTest {
|
||||
|
||||
@Mock
|
||||
private HttpServletRequest request;
|
||||
|
||||
private GuiceFilter guiceFilter;
|
||||
|
||||
@BeforeEach
|
||||
void setUpGuiceFilter() throws ServletException {
|
||||
guiceFilter = new GuiceFilter();
|
||||
|
||||
ServletContext servletContext = mock(ServletContext.class);
|
||||
FilterConfig config = mock(FilterConfig.class);
|
||||
doReturn(servletContext).when(config).getServletContext();
|
||||
|
||||
guiceFilter.init(config);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDownGuiceFilter() {
|
||||
guiceFilter.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateViewControllerForView() {
|
||||
ModuleProvider moduleProvider = SingleView.view("/my-template", 409);
|
||||
when(request.getContextPath()).thenReturn("/scm");
|
||||
|
||||
ViewController instance = findViewController(moduleProvider);
|
||||
assertThat(instance.getTemplate()).isEqualTo("/my-template");
|
||||
|
||||
View view = instance.createView(request);
|
||||
assertThat(view.getStatusCode()).isEqualTo(409);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateViewControllerForError() {
|
||||
ModuleProvider moduleProvider = SingleView.error(new IOException("awesome io"));
|
||||
when(request.getContextPath()).thenReturn("/scm");
|
||||
|
||||
ViewController instance = findViewController(moduleProvider);
|
||||
assertErrorViewController(instance, "awesome io");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBindServlets() {
|
||||
ModuleProvider moduleProvider = SingleView.error(new IOException("awesome io"));
|
||||
Injector injector = Guice.createInjector(moduleProvider.createModules());
|
||||
|
||||
assertThat(injector.getInstance(StaticResourceServlet.class)).isNotNull();
|
||||
assertThat(injector.getInstance(SingleViewServlet.class)).isNotNull();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void assertErrorViewController(ViewController instance, String contains) {
|
||||
assertThat(instance.getTemplate()).isEqualTo("/templates/error.mustache");
|
||||
|
||||
View view = instance.createView(request);
|
||||
assertThat(view.getStatusCode()).isEqualTo(500);
|
||||
assertThat(view.getModel()).isInstanceOfSatisfying(Map.class, map -> {
|
||||
assertThat(map).containsEntry("contextPath", "/scm");
|
||||
String error = (String) map.get("error");
|
||||
assertThat(error).contains(contains);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private ViewController findViewController(ModuleProvider moduleProvider) {
|
||||
Injector injector = Guice.createInjector(moduleProvider.createModules());
|
||||
return injector.getInstance(ViewController.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package sonia.scm.lifecycle;
|
||||
|
||||
import com.google.common.io.Resources;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class StaticResourceServletTest {
|
||||
|
||||
@Mock
|
||||
private HttpServletRequest request;
|
||||
|
||||
@Mock
|
||||
private ServletOutputStream stream;
|
||||
|
||||
@Mock
|
||||
private HttpServletResponse response;
|
||||
|
||||
@Mock
|
||||
private ServletContext context;
|
||||
|
||||
@Test
|
||||
void shouldServeResource() throws IOException {
|
||||
doReturn("/scm").when(request).getContextPath();
|
||||
doReturn("/scm/resource.txt").when(request).getRequestURI();
|
||||
doReturn(context).when(request).getServletContext();
|
||||
URL resource = Resources.getResource("sonia/scm/boot/resource.txt");
|
||||
doReturn(resource).when(context).getResource("/resource.txt");
|
||||
doReturn(stream).when(response).getOutputStream();
|
||||
|
||||
StaticResourceServlet servlet = new StaticResourceServlet();
|
||||
servlet.doGet(request, response);
|
||||
|
||||
verify(response).setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnNotFound() {
|
||||
doReturn("/scm").when(request).getContextPath();
|
||||
doReturn("/scm/resource.txt").when(request).getRequestURI();
|
||||
doReturn(context).when(request).getServletContext();
|
||||
|
||||
StaticResourceServlet servlet = new StaticResourceServlet();
|
||||
servlet.doGet(request, response);
|
||||
|
||||
verify(response).setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package sonia.scm.lifecycle;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junitpioneer.jupiter.TempDirectory;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
||||
@ExtendWith({MockitoExtension.class, TempDirectory.class})
|
||||
class VersionsTest {
|
||||
|
||||
@Mock
|
||||
private SCMContextProvider contextProvider;
|
||||
|
||||
@InjectMocks
|
||||
private Versions versions;
|
||||
|
||||
@Test
|
||||
void shouldReturnTrueForVersionsPreviousTo160(@TempDirectory.TempDir Path directory) throws IOException {
|
||||
setVersion(directory, "1.59");
|
||||
assertThat(versions.isPreviousVersionTooOld()).isTrue();
|
||||
|
||||
setVersion(directory, "1.12");
|
||||
assertThat(versions.isPreviousVersionTooOld()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseForVersion160(@TempDirectory.TempDir Path directory) throws IOException {
|
||||
setVersion(directory, "1.60");
|
||||
assertThat(versions.isPreviousVersionTooOld()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotFailIfVersionContainsLineBreak(@TempDirectory.TempDir Path directory) throws IOException {
|
||||
setVersion(directory, "1.59\n");
|
||||
assertThat(versions.isPreviousVersionTooOld()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseForVersionsNewerAs160(@TempDirectory.TempDir Path directory) throws IOException {
|
||||
setVersion(directory, "1.61");
|
||||
assertThat(versions.isPreviousVersionTooOld()).isFalse();
|
||||
|
||||
setVersion(directory, "1.82");
|
||||
assertThat(versions.isPreviousVersionTooOld()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseForNonExistingVersionFile(@TempDirectory.TempDir Path directory) {
|
||||
setVersionFile(directory.resolve("version.txt"));
|
||||
assertThat(versions.isPreviousVersionTooOld()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWriteNewVersion(@TempDirectory.TempDir Path directory) {
|
||||
Path config = directory.resolve("config");
|
||||
doReturn(config).when(contextProvider).resolve(Paths.get("config"));
|
||||
doReturn("2.0.0").when(contextProvider).getVersion();
|
||||
|
||||
versions.writeNewVersion();
|
||||
|
||||
Path versionFile = config.resolve("version.txt");
|
||||
assertThat(versionFile).exists().hasContent("2.0.0");
|
||||
}
|
||||
|
||||
private void setVersion(Path directory, String version) throws IOException {
|
||||
Path file = directory.resolve("version.txt");
|
||||
Files.write(file, version.getBytes(StandardCharsets.UTF_8));
|
||||
setVersionFile(file);
|
||||
}
|
||||
|
||||
private void setVersionFile(Path file) {
|
||||
doReturn(file).when(contextProvider).resolve(Paths.get("config", "version.txt"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user