/* * Copyright 2017 ThoughtWorks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.thoughtworks.go.server; import com.thoughtworks.go.helpers.FileSystemUtils; import com.thoughtworks.go.util.ClassMockery; import com.thoughtworks.go.util.SubprocessLogger; import com.thoughtworks.go.util.SystemEnvironment; import com.thoughtworks.go.util.TestFileUtil; import com.thoughtworks.go.util.validators.Validation; import org.hamcrest.CoreMatchers; import org.jmock.Expectations; import org.jmock.Mockery; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import javax.net.ssl.SSLSocketFactory; import java.io.File; import java.util.Arrays; import java.util.List; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.core.IsNot.not; import static org.hamcrest.core.IsNull.nullValue; import static org.junit.Assert.*; import static org.mockito.Mockito.*; @RunWith(org.jmock.integration.junit4.JMock.class) public class GoServerTest { Mockery context = new ClassMockery(); private SystemEnvironment systemEnvironment; @Before public void setUp() throws Exception { systemEnvironment = new SystemEnvironment(); systemEnvironment.set(SystemEnvironment.APP_SERVER, AppServerStub.class.getCanonicalName()); } @Test public void shouldValidateOnServerStartup() throws Exception { final SystemEnvironment systemEnvironment = context.mock(SystemEnvironment.class); StubGoServer goServer = new StubGoServer(systemEnvironment, Validation.SUCCESS); goServer.subprocessLogger = mock(SubprocessLogger.class); final File tmpFile = TestFileUtil.createTempFile("keystore.tmp"); tmpFile.deleteOnExit(); context.checking(new Expectations() { { allowing(systemEnvironment).getServerPort(); will(returnValue(9153)); allowing(systemEnvironment).getSslServerPort(); will(returnValue(9443)); allowing(systemEnvironment).keystore(); will(returnValue(tmpFile)); allowing(systemEnvironment).truststore(); will(returnValue(tmpFile)); allowing(systemEnvironment).agentkeystore(); will(returnValue(tmpFile)); } }); goServer.go(); assertThat(goServer.wasStarted(), is(true)); } @Test public void shouldRegisterSubprocessLoggerAsExit() throws Exception { SystemEnvironment systemEnvironment = mock(SystemEnvironment.class); Validation validation = mock(Validation.class); when(validation.isSuccessful()).thenReturn(true); StubGoServer goServer = new StubGoServer(systemEnvironment, validation); goServer.subprocessLogger = mock(SubprocessLogger.class); goServer.go(); verify(goServer.subprocessLogger).registerAsExitHook("Following processes were alive at shutdown: "); } @Test public void shouldCreateASubprocessLoggerInConstructor() { GoServer goServer = new GoServer(); assertThat(goServer.subprocessLogger, not(nullValue())); } @Test public void shouldNotStartServerIfValidationFails() throws Exception { final SystemEnvironment systemEnvironment = context.mock(SystemEnvironment.class); Validation validation = new Validation().addError(new Exception("Server Port occupied")); StubGoServer goServer = new StubGoServer(systemEnvironment, validation); goServer.go(); assertThat(goServer.wasStarted(), is(false)); } @Test public void shouldStartAppServer() throws Exception { SystemEnvironment systemEnvironment = new SystemEnvironment(); systemEnvironment.set(SystemEnvironment.APP_SERVER, AppServerStub.class.getCanonicalName()); GoServer goServer = new GoServer(); goServer.startServer(); AppServer appServer = (AppServer) com.thoughtworks.go.util.ReflectionUtil.getField(goServer, "server"); assertThat(appServer instanceof AppServerStub, is(true)); AppServerStub appServerStub = (AppServerStub) appServer; assertThat(appServerStub.calls.get("addExtraJarsToClasspath"), is("")); assertThat(appServerStub.calls.get("setCookieExpirePeriod"), is(1209600)); assertThat(appServerStub.calls.get("getUnavailableException"), is(true)); assertThat(appServerStub.calls.get("configure"), is(true)); assertThat(appServerStub.calls.get("start"), is(true)); assertThat(appServerStub.calls.get("stop"), is(CoreMatchers.nullValue())); goServer.stop(); assertThat(appServerStub.calls.get("stop"), is(true)); } @Test public void shouldStopServerAndThrowExceptionWhenServerFailsToStartWithAnUnhandledException() throws Exception { final AppServer server = mock(AppServer.class); when(server.getUnavailableException()).thenReturn(new RuntimeException("Some unhandled server startup exception")); GoServer goServer = new GoServer(){ @Override AppServer configureServer() throws Exception { return server; } }; doNothing().when(server).start(); doNothing().when(server).stop(); try { goServer.startServer(); fail("Should have thrown an exception"); } catch (RuntimeException e) { assertThat(e.getMessage(), is("Failed to start Go server.")); assertThat(e.getCause().getMessage(), is("Some unhandled server startup exception")); } verify(server).start(); verify(server).getUnavailableException(); verify(server).stop(); } @Test public void shouldLoadAllJarsInTheAddonsDirectoryIntoClassPath() throws Exception { File addonsDirectory = createInAddonDir("some-addon-dir"); FileSystemUtils.createFile("addon-1.JAR", addonsDirectory); FileSystemUtils.createFile("addon-2.jar", addonsDirectory); FileSystemUtils.createFile("addon-3.jAR", addonsDirectory); FileSystemUtils.createFile("some-file-which-does-not-end-with-dot-jar.txt", addonsDirectory); File oneAddonDirectory = createInAddonDir("one-addon-dir"); FileSystemUtils.createFile("addon-1.jar", oneAddonDirectory); File noAddonDirectory = createInAddonDir("no-addon-dir"); SSLSocketFactory sslSocketFactory = mock(SSLSocketFactory.class); when(sslSocketFactory.getSupportedCipherSuites()).thenReturn(new String[0]); GoServer goServerWithMultipleAddons = new GoServer(setAddonsPathTo(addonsDirectory), sslSocketFactory); goServerWithMultipleAddons.startServer(); AppServerStub appServer = (AppServerStub) com.thoughtworks.go.util.ReflectionUtil.getField(goServerWithMultipleAddons, "server"); assertExtraClasspath(appServer, "test-addons/some-addon-dir/addon-1.JAR", "test-addons/some-addon-dir/addon-2.jar", "test-addons/some-addon-dir/addon-3.jAR"); GoServer goServerWithOneAddon = new GoServer(setAddonsPathTo(oneAddonDirectory), sslSocketFactory); goServerWithOneAddon.startServer(); appServer = (AppServerStub) com.thoughtworks.go.util.ReflectionUtil.getField(goServerWithOneAddon, "server"); assertExtraClasspath(appServer, "test-addons/one-addon-dir/addon-1.jar"); GoServer goServerWithNoAddon = new GoServer(setAddonsPathTo(noAddonDirectory), sslSocketFactory); goServerWithNoAddon.startServer(); appServer = (AppServerStub) com.thoughtworks.go.util.ReflectionUtil.getField(goServerWithNoAddon, "server"); assertExtraClasspath(appServer, ""); GoServer goServerWithInaccessibleAddonDir = new GoServer(setAddonsPathTo(new File("non-existent-directory")), sslSocketFactory); goServerWithInaccessibleAddonDir.startServer(); appServer = (AppServerStub) com.thoughtworks.go.util.ReflectionUtil.getField(goServerWithNoAddon, "server"); assertExtraClasspath(appServer, ""); } @Test public void shouldTurnOffJrubyObjectProxyCacheByDefault(){ new GoServer(); assertThat(new SystemEnvironment().getPropertyImpl("jruby.ji.objectProxyCache"), is("false")); } private void assertExtraClasspath(AppServerStub appServer, String... expectedClassPathJars) { String extraJars = (String) appServer.calls.get("addExtraJarsToClasspath"); List<String> actualExtraClassPath = Arrays.asList(extraJars.split(",")); assertEquals("Number of jars wrong. Expected: " + Arrays.asList(expectedClassPathJars) + ". Actual: " + actualExtraClassPath, expectedClassPathJars.length, actualExtraClassPath.size()); for (String expectedClassPathJar : expectedClassPathJars) { String platformIndependantNameOfExpectedJar = expectedClassPathJar.replace("/", File.separator); assertTrue("Expected " + extraJars + " to contain: " + platformIndependantNameOfExpectedJar, actualExtraClassPath.contains(platformIndependantNameOfExpectedJar)); } } private File createInAddonDir(String dirInsideAddonDir) { File addonDir = new File("test-addons"); File dirWhichWillContainAddons = new File(addonDir, dirInsideAddonDir); dirWhichWillContainAddons.mkdirs(); return dirWhichWillContainAddons; } private SystemEnvironment setAddonsPathTo(File path) { SystemEnvironment systemEnvironment = mock(SystemEnvironment.class); when(systemEnvironment.get(SystemEnvironment.APP_SERVER)).thenReturn(AppServerStub.class.getCanonicalName()); doReturn(path.getPath()).when(systemEnvironment).get(SystemEnvironment.ADDONS_PATH); return systemEnvironment; } private class StubGoServer extends GoServer { private boolean wasStarted = false; private Validation validation; public StubGoServer(SystemEnvironment systemEnvironment, Validation validation) { super(systemEnvironment,null); this.validation = validation; } @Override protected void startServer() throws Exception { wasStarted = true; } public Boolean wasStarted() { return wasStarted; } @Override Validation validate() { return validation; } } }