/** * Copyright (c) Codice Foundation * <p> * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation, either version 3 of the * License, or any later version. * <p> * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. */ package org.codice.ddf.admin.application.service.impl; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.net.URI; import java.net.URL; import java.util.EnumSet; import java.util.concurrent.Callable; import org.apache.karaf.features.FeaturesService; import org.apache.karaf.features.FeaturesService.Option; import org.codice.ddf.admin.application.service.ApplicationService; import org.codice.ddf.admin.application.service.ApplicationServiceException; import org.junit.Test; import org.mockito.ArgumentMatcher; import org.mockito.Matchers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.core.Appender; import ddf.security.Subject; /** * Tests the application config installer code */ public class ApplicationConfigInstallerTest { private static final String START_FEATURE = "startFeature"; private static final String STOP_FEATURE = "stopFeature"; private static final String BAD_FILE = "NOFILE.properties"; private static final String GOOD_FILE = "applist.properties"; private static final String EMPTY_FILE = "empty_applist.properties"; private static final String INSTALL_FILE = "install_applist.properties"; private static final String INVALID_FILE = "invalid_applist.properties"; private static final String RUN_ASE_EX = "Could not start"; private static final String RUN_NO_CONFIG = "No config file located, cannot load from it."; private static final String RUN_INVALID_URI = "Could not install application, location is not a valid URI"; /** * Tests with a valid file that contains one application that all of the * services were called correctly. * * @throws Exception */ @Test public void testFileValid() throws Exception { FeaturesService featuresService = mock(FeaturesService.class); ApplicationService appService = mock(ApplicationService.class); URL fileURL = this.getClass() .getResource("/" + GOOD_FILE); ApplicationConfigInstaller configInstaller = getApplicationConfigInstaller(fileURL.getFile(), appService, featuresService, START_FEATURE, STOP_FEATURE); configInstaller.run(); // verify that the correct application was started verify(appService, never()).addApplication(any(URI.class)); verify(appService).startApplication("solr-app"); // verify the post start and post stop features were called verify(featuresService).installFeature(START_FEATURE, EnumSet.of(Option.NoAutoRefreshBundles)); verify(featuresService).uninstallFeature(STOP_FEATURE); } /** * Tests with a valid file that contains one non-local application that all of the * services were called correctly. * * @throws Exception */ @Test public void testFileInstall() throws Exception { FeaturesService featuresService = mock(FeaturesService.class); ApplicationService appService = mock(ApplicationService.class); URL fileURL = this.getClass() .getResource("/" + INSTALL_FILE); ApplicationConfigInstaller configInstaller = getApplicationConfigInstaller(fileURL.getFile(), appService, featuresService, START_FEATURE, STOP_FEATURE); configInstaller.run(); // verify that the correct application was added and then started verify(appService).addApplication(new URI("file:/location/to/solr-app-1.0.0.kar")); verify(appService).startApplication("solr-app"); // verify the post start and post stop features were called verify(featuresService).installFeature(START_FEATURE, EnumSet.of(Option.NoAutoRefreshBundles)); verify(featuresService).uninstallFeature(STOP_FEATURE); } /** * Tests the use case that there is a file but it does not have any apps * listed in it (they are commented out). The test should not call the * features stop and start since no apps were loaded. * * @throws Exception */ @Test public void testFileEmpty() throws Exception { FeaturesService featuresService = mock(FeaturesService.class); ApplicationService appService = mock(ApplicationService.class); URL fileURL = this.getClass() .getResource("/" + EMPTY_FILE); ApplicationConfigInstaller configInstaller = getApplicationConfigInstaller(fileURL.getFile(), appService, featuresService, START_FEATURE, STOP_FEATURE); configInstaller.run(); // verify that the app service was never called verify(appService, never()).addApplication(any(URI.class)); verify(appService, never()).startApplication(anyString()); // verify the post start and post stop features were not called verify(featuresService, never()).installFeature(anyString(), any(EnumSet.class)); verify(featuresService, never()).uninstallFeature(anyString()); } /** * Tests that when a file is not found, the post install start and post * install stop features are NOT called. */ @Test public void testFileNotValid() throws Exception { FeaturesService featuresService = mock(FeaturesService.class); ApplicationConfigInstaller configInstaller = getApplicationConfigInstaller(BAD_FILE, null, featuresService, START_FEATURE, STOP_FEATURE); configInstaller.run(); // verify the post start and post stop features were not called verify(featuresService, never()).installFeature(anyString(), any(EnumSet.class)); verify(featuresService, never()).uninstallFeature(anyString()); } /** * Tests the {@link ApplicationConfigInstaller#run()} method for the case * where an ApplicationServiceException is thrown by appService.addApplication(..) * * @throws Exception */ // TODO RAP 29 Aug 16: DDF-2443 - Fix test to not depend on specific log output @Test public void testRunASE() throws Exception { ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); final Appender mockAppender = mock(Appender.class); when(mockAppender.getName()).thenReturn("MOCK"); root.addAppender(mockAppender); FeaturesService featuresService = mock(FeaturesService.class); ApplicationService testAppService = mock(ApplicationServiceImpl.class); doThrow(new ApplicationServiceException()).when(testAppService) .startApplication(anyString()); URL fileURL = this.getClass() .getResource("/" + GOOD_FILE); ApplicationConfigInstaller configInstaller = getApplicationConfigInstaller(fileURL.getFile(), testAppService, featuresService, START_FEATURE, STOP_FEATURE); configInstaller.run(); verify(mockAppender).doAppend(argThat(new ArgumentMatcher() { @Override public boolean matches(final Object argument) { return ((LoggingEvent) argument).getFormattedMessage() .contains(RUN_ASE_EX); } })); } /** * Tests the {@link ApplicationConfigInstaller#run()} method for the case * where the configFile doesn't exist * * @throws Exception */ // TODO RAP 29 Aug 16: DDF-2443 - Fix test to not depend on specific log output @Test public void testRunConfigFileNotExist() throws Exception { ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); final Appender mockAppender = mock(Appender.class); when(mockAppender.getName()).thenReturn("MOCK"); root.addAppender(mockAppender); root.setLevel(Level.ALL); ApplicationConfigInstaller configInstaller = getApplicationConfigInstaller(BAD_FILE, null, null, START_FEATURE, STOP_FEATURE); configInstaller.run(); verify(mockAppender).doAppend(argThat(new ArgumentMatcher() { @Override public boolean matches(final Object argument) { return ((LoggingEvent) argument).getFormattedMessage() .contains(RUN_NO_CONFIG); } })); } /** * Tests the {@link ApplicationConfigInstaller#run()} method to test if it handles * an invalid URI cleanly * * @throws Exception */ // TODO RAP 29 Aug 16: DDF-2443 - Fix test to not depend on specific log output @Test public void testRunInvalidURI() throws Exception { ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); final Appender mockAppender = mock(Appender.class); when(mockAppender.getName()).thenReturn("MOCK"); root.addAppender(mockAppender); ApplicationServiceImpl testAppService = mock(ApplicationServiceImpl.class); URL fileURL = this.getClass() .getResource("/" + INVALID_FILE); ApplicationConfigInstaller configInstaller = getApplicationConfigInstaller(fileURL.getFile(), testAppService, null, START_FEATURE, STOP_FEATURE); configInstaller.run(); verify(mockAppender).doAppend(argThat(new ArgumentMatcher() { @Override public boolean matches(final Object argument) { return ((LoggingEvent) argument).getFormattedMessage() .contains(RUN_INVALID_URI); } })); } ApplicationConfigInstaller getApplicationConfigInstaller(String fileName, ApplicationService appService, FeaturesService featuresService, String postInstallFeatureStart, String postInstallFeatureStop) { return new ApplicationConfigInstaller(fileName, appService, featuresService, postInstallFeatureStart, postInstallFeatureStop) { @SuppressWarnings("unchecked") @Override public Subject getSystemSubject() { Subject subject = mock(Subject.class); when(subject.execute(Matchers.<Callable<Object>>any())).thenAnswer(invocation -> { Callable<Object> callable = (Callable<Object>) invocation.getArguments()[0]; return callable.call(); }); return subject; } }; } }