/** * Copyright 2013 the original author or authors. * * 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 io.neba.core.logviewer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.thread.ThreadPool; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import org.osgi.framework.BundleContext; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URL; import java.util.Collection; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import static io.neba.core.util.ZipFileUtil.toZipFileEntryName; import static org.apache.commons.io.FileUtils.listFiles; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.any; import static org.mockito.Mockito.*; /** * @author Olaf Otto */ @RunWith(MockitoJUnitRunner.class) public class LogfileViewerConsolePluginTest { @Mock private HttpServletRequest request; @Mock private HttpServletResponse response; @Mock private BundleContext bundleContext; @Mock private ServletOutputStream outputStream; @Mock private ServletConfig config; @Mock private ContextHandler.Context context; @Mock private ContextHandler contextHandler; @Mock private Server server; @Mock private LogFiles logFiles; @Mock private TailServlet tailServlet; private File testLogfileDirectory; private StringWriter internalWriter; private String htmlResponse; private ByteArrayOutputStream internalOutputStream; private ZipInputStream zippedFiles; private Collection<File> availableLogFiles; @InjectMocks private LogfileViewerConsolePlugin testee; @Before public void setUp() throws Exception { this.internalWriter = new StringWriter(); this.internalOutputStream = new ByteArrayOutputStream(8192); PrintWriter writer = new PrintWriter(this.internalWriter); URL testLogfileUrl = getClass().getResource("/io/neba/core/logviewer/testlogfiles/"); this.testLogfileDirectory = new File(testLogfileUrl.getFile()); this.availableLogFiles = listFiles(this.testLogfileDirectory, null, true); when(this.context.getContextHandler()).thenReturn(contextHandler); when(this.contextHandler.getServer()).thenReturn(this.server); when(this.server.getThreadPool()).thenReturn(mock(ThreadPool.class)); when(this.request.getServletPath()).thenReturn("/system/console"); when(this.request.getServerName()).thenReturn("servername"); when(this.request.getMethod()).thenReturn("GET"); when(this.request.getProtocol()).thenReturn("HTTP"); when(this.response.getWriter()).thenReturn(writer); when(this.response.getOutputStream()).thenReturn(this.outputStream); Answer<Object> writeIntToByteArrayOutputStream = invocation -> { internalOutputStream.write((Integer) invocation.getArguments()[0]); return null; }; Answer<Object> writeBytesToByteArrayOutputStream = invocation -> { byte[] b = (byte[]) invocation.getArguments()[0]; int off = (Integer) invocation.getArguments()[1]; int len = (Integer) invocation.getArguments()[2]; internalOutputStream.write(b, off, len); return null; }; doAnswer(writeIntToByteArrayOutputStream).when(this.outputStream).write(anyInt()); doAnswer(writeBytesToByteArrayOutputStream).when(this.outputStream).write(isA(byte[].class), anyInt(), anyInt()); when(this.config.getServletContext()).thenReturn(this.context); when(this.logFiles.resolveLogFiles()).thenReturn(availableLogFiles); this.testee.init(config); } @Test public void testGetResources() throws Exception { URL resource = this.testee.getResource("/logviewer/static/testresource.txt"); assertThat(resource).isNotNull(); } @Test public void testRenderContentContainsDropdownValuesForTestLogfiles() throws Exception { renderContent(); assertHtmlResponseContains("value=\"" + pathOf("logs/error.log") + "\""); assertHtmlResponseContains("value=\"" + pathOf("logs/error.log.1") + "\""); assertHtmlResponseContains("value=\"" + pathOf("logs/error.log.2020-01-01") + "\""); assertHtmlResponseContains("value=\"" + pathOf("logs/crx/error.log") + "\""); assertHtmlResponseContains("value=\"" + pathOf("logs/crx/error.log.0") + "\""); assertHtmlResponseContains("value=\"" + pathOf("logs/crx/error.log.2020-11-23") + "\""); } @Test public void testDownloadLogFilesAsZip() throws Exception { getZippedLogfiles(); verifyLogFilesAreSendAs("logfiles-servername.zip"); for (File file : this.availableLogFiles) { assertNextZipEntryIs(toZipFileEntryName(file)); } } @Test public void testDestroy() throws Exception { destroy(); verifyTailServletIsDestroyed(); } @Test(expected = ServletException.class) public void testRuntimeExceptionsDuringTailServletInitializationAreConvertedToServletException() throws Exception { doThrow(new RuntimeException("THIS IS AN EXPECTED TEST EXCEPTION")).when(this.tailServlet).init(any()); this.testee.init(this.config); } @Test public void testDelegationOfTailRequestsToTailServlet() throws Exception { withRequestPath("/system/console/logviewer/tail"); doGet(); verifyRequestIsDelegatedToTailServlet(); } private void verifyRequestIsDelegatedToTailServlet() throws ServletException, IOException { verify(this.tailServlet).service(this.request, this.response); } private void verifyTailServletIsDestroyed() { verify(this.tailServlet).destroy(); } private void destroy() { this.testee.destroy(); } private void verifyLogFilesAreSendAs(String filename) { verify(this.response).setHeader(eq("Content-Disposition"), eq("attachment;filename=" + filename)); } private void assertNextZipEntryIs(String expected) throws IOException { ZipEntry nextEntry = this.zippedFiles.getNextEntry(); assertThat(nextEntry).isNotNull(); assertThat(nextEntry.getName()).isEqualTo(expected); } private void getZippedLogfiles() throws ServletException, IOException { withRequestPath("/system/console/logviewer/download"); doGet(); this.zippedFiles = new ZipInputStream(new ByteArrayInputStream(this.internalOutputStream.toByteArray())); } private void doGet() throws ServletException, IOException { this.testee.doGet(this.request, this.response); this.htmlResponse = this.internalWriter.toString(); } private void withRequestPath(String requestPath) { when(this.request.getRequestURI()).thenReturn(requestPath); when(this.request.getPathInfo()).thenReturn(requestPath); } private void assertHtmlResponseContains(String expected) { assertThat(this.htmlResponse).contains(expected); } private void renderContent() throws ServletException, IOException { this.testee.renderContent(this.request, this.response); this.htmlResponse = this.internalWriter.getBuffer().toString(); } private String pathOf(String relativePath) { return new File(this.testLogfileDirectory, relativePath).getAbsolutePath(); } }