/** * 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 ddf.catalog.event.retrievestatus; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.UUID; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.session.mgt.SimpleSession; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.SimplePrincipalCollection; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.ThreadContext; import org.codice.ddf.activities.ActivityEvent; import org.codice.ddf.notifications.Notification; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.osgi.service.event.Event; import org.osgi.service.event.EventAdmin; import org.slf4j.Logger; import com.google.common.collect.ImmutableList; import ddf.action.ActionProvider; import ddf.catalog.data.Metacard; import ddf.catalog.event.retrievestatus.DownloadsStatusEventPublisher.ProductRetrievalStatus; import ddf.catalog.operation.ResourceRequest; import ddf.catalog.operation.ResourceResponse; import ddf.catalog.resource.Resource; /** * Abstract class used to test the DownloadsStatusEventPublisher. <br/> * <br/> * This class contains test cases to verify that the event admin service is * properly called after a download event is triggered. Given the different * types of events, implementations of this test should set up the publisher to * match their specific event type. * * */ public abstract class AbstractDownloadsStatusEventPublisherTest { protected static final Logger LOGGER = org.slf4j.LoggerFactory .getLogger(AbstractDownloadsStatusEventPublisherTest.class); private static final String USER_ID = "testSubjectUser"; private static final String DEFAULT_USER_ID = ""; private static final String SESSION_ID = "123456"; protected static EventAdmin eventAdmin; protected static ActionProvider actionProvider; protected static Metacard metacard; protected static ResourceResponse resourceResponse; protected static ResourceRequest resourceRequest; protected static Resource resource; protected static Map<String, Serializable> properties; protected static DownloadsStatusEventPublisher publisher; protected static String downloadIdentifier; protected Event curEvent; @BeforeClass public static void oneTimeSetup() { resourceResponse = mock(ResourceResponse.class); resourceRequest = mock(ResourceRequest.class); resource = mock(Resource.class); properties = new HashMap<String, Serializable>(); properties.put(Notification.NOTIFICATION_KEY_USER_ID, USER_ID); properties.put(Notification.NOTIFICATION_KEY_SESSION_ID, SESSION_ID); when(resource.getName()).thenReturn("testCometDSessionID"); when(resourceRequest.getProperties()).thenReturn(properties); when(resourceRequest.containsPropertyName(Notification.NOTIFICATION_KEY_USER_ID)) .thenReturn(true); when(resourceRequest.getPropertyValue(Notification.NOTIFICATION_KEY_USER_ID)) .thenReturn(properties.get(Notification.NOTIFICATION_KEY_USER_ID)); when(resourceRequest.containsPropertyName(Notification.NOTIFICATION_KEY_SESSION_ID)) .thenReturn(true); when(resourceRequest.getPropertyValue(Notification.NOTIFICATION_KEY_SESSION_ID)) .thenReturn(properties.get(Notification.NOTIFICATION_KEY_SESSION_ID)); when(resourceResponse.getResource()).thenReturn(resource); when(resourceResponse.getRequest()).thenReturn(resourceRequest); } @Before public void setUpTest() { eventAdmin = mock(EventAdmin.class); Mockito.doAnswer(new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); curEvent = (Event) args[0]; return null; } }).when(eventAdmin).postEvent(any(Event.class)); metacard = mock(Metacard.class); when(metacard.getId()).thenReturn("12345"); downloadIdentifier = UUID.randomUUID().toString(); } @After public void tearDownTest() { // Remove the security from the thread after every test ThreadContext.unbindSecurityManager(); ThreadContext.unbindSubject(); } /** * Calls the retrieval status test with a security subject */ @org.junit.Test public void testPostRetrievalStatusWithSecurity() { setupPublisher(); addSecurity(); testPostRetrievalStatus(USER_ID); } /** * Calls the retrieval status test with no security subject (simulating no * security in the system). */ @org.junit.Test public void testPostRetrievalStatusWithoutSecurity() { setupPublisher(); testPostRetrievalStatus(DEFAULT_USER_ID); } /** * Tests that the retrieval status is properly sent to the event admin. * * @param correctUser * user to check for in the event property */ private void testPostRetrievalStatus(String correctUser) { publisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.CANCELLED, metacard, "test detail", 20L, downloadIdentifier); verify(eventAdmin, times(1)).postEvent(any(Event.class)); assertEquals(correctUser, curEvent.getProperty(Notification.NOTIFICATION_KEY_USER_ID)); publisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.FAILED, metacard, "test detail", 250L, downloadIdentifier); verify(eventAdmin, times(2)).postEvent(any(Event.class)); assertEquals(correctUser, curEvent.getProperty(Notification.NOTIFICATION_KEY_USER_ID)); publisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.RETRYING, metacard, "test detail", 350L, downloadIdentifier); verify(eventAdmin, times(3)).postEvent(any(Event.class)); assertEquals(correctUser, curEvent.getProperty(Notification.NOTIFICATION_KEY_USER_ID)); publisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.COMPLETE, metacard, "test detail", 500L, downloadIdentifier); verify(eventAdmin, times(4)).postEvent(any(Event.class)); assertEquals(correctUser, curEvent.getProperty(Notification.NOTIFICATION_KEY_USER_ID)); } /** * Verifies client can set parameter to only publish a notification, no corresponding activity. */ @org.junit.Test public void testPostRetrievalStatusNotificationOnly() { setupPublisher(); publisher.setActivityEnabled(true); publisher.setNotificationEnabled(true); publisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.CANCELLED, metacard, "test detail", 20L, downloadIdentifier, true, false); // By expecting only 1 invocation of postEvent(), this verifies only a notification was sent, // and no activity. No way to check absence of ActivityEvent. verify(eventAdmin, times(1)).postEvent(any(Event.class)); assertEquals(DEFAULT_USER_ID, curEvent.getProperty(Notification.NOTIFICATION_KEY_USER_ID)); } /** * Verifies client can set parameter to only publish an activity, no corresponding notification. */ @org.junit.Test public void testPostRetrievalStatusActivityOnly() { setupPublisher(); publisher.setActivityEnabled(true); publisher.setNotificationEnabled(true); publisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.CANCELLED, metacard, "test detail", 20L, downloadIdentifier, false, true); // By expecting only 1 invocation of postEvent(), this verifies only an activity was sent, // and no notification. No way to check absence of Notification. verify(eventAdmin, times(1)).postEvent(any(Event.class)); assertEquals(DEFAULT_USER_ID, curEvent.getProperty(ActivityEvent.USER_ID_KEY)); } /** * Verifies client can set parameters to publish an activity and its corresponding notification. * This is the default behavior. */ @org.junit.Test public void testPostRetrievalStatusSendNotificationAndActivity() { setupPublisher(); publisher.setActivityEnabled(true); publisher.setNotificationEnabled(true); publisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.CANCELLED, metacard, "test detail", 20L, downloadIdentifier, true, true); // Expect 2 invocations of postEvent(), one for Notification and one for ActivityEvent verify(eventAdmin, times(2)).postEvent(any(Event.class)); assertEquals(DEFAULT_USER_ID, curEvent.getProperty(ActivityEvent.USER_ID_KEY)); assertEquals(DEFAULT_USER_ID, curEvent.getProperty(Notification.NOTIFICATION_KEY_USER_ID)); // Also verifying the method with default values to always send both notification and activity // is working. publisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.CANCELLED, metacard, "test detail", 20L, downloadIdentifier); verify(eventAdmin, times(4)).postEvent(any(Event.class)); assertEquals(DEFAULT_USER_ID, curEvent.getProperty(ActivityEvent.USER_ID_KEY)); assertEquals(DEFAULT_USER_ID, curEvent.getProperty(Notification.NOTIFICATION_KEY_USER_ID)); } /** * Calls the no name property test with a security subject (simulating * security in the system). */ @org.junit.Test public void testPostRetrievalStatusWithNoNamePropertyWithSecurity() { setupPublisher(); addSecurity(); testPostRetrievalStatusWithNoNameProperty(USER_ID); } /** * Calls the no name property test with no security subject (simulating no * security in the system). */ @org.junit.Test public void testPostRetrievalStatusWithNoNamePropertyWithoutSecurity() { setupPublisher(); testPostRetrievalStatusWithNoNameProperty(DEFAULT_USER_ID); } /** * Tests that the event admin is properly called when a status is sent with * no name property. * * @param correctUser * user to check for in the event property */ private void testPostRetrievalStatusWithNoNameProperty(String correctUser) { publisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.CANCELLED, metacard, "test detail", 20L, downloadIdentifier); verify(eventAdmin, times(1)).postEvent(any(Event.class)); assertEquals(correctUser, curEvent.getProperty(Notification.NOTIFICATION_KEY_USER_ID)); publisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.FAILED, metacard, "test detail", 250L, downloadIdentifier); verify(eventAdmin, times(2)).postEvent(any(Event.class)); assertEquals(correctUser, curEvent.getProperty(Notification.NOTIFICATION_KEY_USER_ID)); publisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.RETRYING, metacard, "test detail", 350L, downloadIdentifier); verify(eventAdmin, times(3)).postEvent(any(Event.class)); assertEquals(correctUser, curEvent.getProperty(Notification.NOTIFICATION_KEY_USER_ID)); publisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.COMPLETE, metacard, "test detail", 500L, downloadIdentifier); verify(eventAdmin, times(4)).postEvent(any(Event.class)); assertEquals(correctUser, curEvent.getProperty(Notification.NOTIFICATION_KEY_USER_ID)); } @org.junit.Test public void testPostRetrievalWithNoStatus() { setupPublisherWithNoNotifications(); publisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.STARTED, metacard, null, 0L, downloadIdentifier); verify(eventAdmin, times(0)).postEvent(any(Event.class)); publisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.STARTED, metacard, "test detail", 10L, downloadIdentifier); verify(eventAdmin, times(0)).postEvent(any(Event.class)); publisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.CANCELLED, metacard, "test detail", 20L, downloadIdentifier); verify(eventAdmin, times(0)).postEvent(any(Event.class)); publisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.FAILED, metacard, "test detail", 250L, downloadIdentifier); verify(eventAdmin, times(0)).postEvent(any(Event.class)); publisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.RETRYING, metacard, "test detail", 350L, downloadIdentifier); verify(eventAdmin, times(0)).postEvent(any(Event.class)); publisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.COMPLETE, metacard, "test detail", 500L, downloadIdentifier); verify(eventAdmin, times(0)).postEvent(any(Event.class)); } protected abstract void setupPublisher(); private void setupPublisherWithNoNotifications() { actionProvider = mock(ActionProvider.class); eventAdmin = mock(EventAdmin.class); publisher = new DownloadsStatusEventPublisher(eventAdmin, ImmutableList.of(actionProvider)); publisher.setNotificationEnabled(false); publisher.setActivityEnabled(false); } private void addSecurity() { org.apache.shiro.mgt.SecurityManager secManager = new DefaultSecurityManager(); PrincipalCollection principals = new SimplePrincipalCollection(USER_ID, "testrealm"); Subject subject = new Subject.Builder(secManager).principals(principals) .session(new SimpleSession()).authenticated(true).buildSubject(); ThreadContext.bind(secManager); ThreadContext.bind(subject); } }