/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* 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 2.1 of
* the License, or (at your option) any later version.
*
* This software 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.notifications.internal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.xwiki.bridge.DocumentAccessBridge;
import org.xwiki.eventstream.Event;
import org.xwiki.eventstream.EventStream;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.DocumentReferenceResolver;
import org.xwiki.notifications.CompositeEvent;
import org.xwiki.notifications.NotificationException;
import org.xwiki.notifications.NotificationPreference;
import org.xwiki.query.Query;
import org.xwiki.security.authorization.AuthorizationManager;
import org.xwiki.security.authorization.Right;
import org.xwiki.test.annotation.ComponentList;
import org.xwiki.test.mockito.MockitoComponentMockingRule;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
/**
* @version $Id: de0ff92415c85c0d9bd196b8fcba4c9e0116b327 $
*/
@ComponentList(SimilarityCalculator.class)
public class DefaultNotificationManagerTest
{
@Rule
public final MockitoComponentMockingRule<DefaultNotificationManager> mocker =
new MockitoComponentMockingRule<>(DefaultNotificationManager.class);
private EventStream eventStream;
private QueryGenerator queryGenerator;
private DocumentAccessBridge documentAccessBridge;
private DocumentReferenceResolver<String> documentReferenceResolver;
private ModelBridge modelBridge;
private AuthorizationManager authorizationManager;
private DocumentReference userReference = new DocumentReference("xwiki", "XWiki", "UserA");
private Query query;
private Date startDate;
@Before
public void setUp() throws Exception
{
eventStream = mocker.getInstance(EventStream.class);
queryGenerator = mocker.getInstance(QueryGenerator.class);
documentAccessBridge = mocker.getInstance(DocumentAccessBridge.class);
documentReferenceResolver = mocker.getInstance(DocumentReferenceResolver.TYPE_STRING);
modelBridge = mocker.getInstance(ModelBridge.class);
authorizationManager = mocker.getInstance(AuthorizationManager.class);
startDate = new Date(10);
when(documentReferenceResolver.resolve("xwiki:XWiki.UserA")).thenReturn(userReference);
query = mock(Query.class);
when(queryGenerator.generateQuery(any(DocumentReference.class), anyBoolean(), nullable(Date.class), nullable(List.class)))
.thenReturn(query);
when(modelBridge.getUserStartDate(userReference)).thenReturn(startDate);
NotificationPreference pref1 = new NotificationPreference("create", null, true);
when(modelBridge.getNotificationsPreferences(userReference)).thenReturn(Arrays.asList(pref1));
}
@Test
public void getEventsWith2Queries() throws Exception
{
// Mocks
Event event1 = mock(Event.class);
Event event2 = mock(Event.class);
Event event3 = mock(Event.class);
Event event4 = mock(Event.class);
Event event5 = mock(Event.class);
Event event6 = mock(Event.class);
DocumentReference doc1 = new DocumentReference("xwiki", "Main", "WebHome");
when(event1.getDocument()).thenReturn(doc1);
DocumentReference doc2 = new DocumentReference("xwiki", "PrivateSpace", "WebHome");
when(event2.getDocument()).thenReturn(doc2);
when(event3.getDocument()).thenReturn(doc2);
when(event4.getDocument()).thenReturn(doc2);
when(authorizationManager.hasAccess(Right.VIEW, userReference, doc1)).thenReturn(true);
when(authorizationManager.hasAccess(Right.VIEW, userReference, doc2)).thenReturn(false);
when(event1.getType()).thenReturn("type1");
when(event2.getType()).thenReturn("type2");
when(event3.getType()).thenReturn("type3");
when(event4.getType()).thenReturn("type4");
when(event5.getType()).thenReturn("type5");
when(event6.getType()).thenReturn("type6");
when(eventStream.searchEvents(query)).thenReturn(Arrays.asList(event1, event2, event3, event4),
Arrays.asList(event5, event6));
// Test
List<CompositeEvent> results
= mocker.getComponentUnderTest().getEvents("xwiki:XWiki.UserA", true, 2);
// Verify
assertEquals(2, results.size());
assertEquals(event1, results.get(0).getEvents().get(0));
assertEquals(event5, results.get(1).getEvents().get(0));
}
@Test
public void getEventsWhenNoPreferences() throws Exception
{
NotificationPreference pref1 = new NotificationPreference("create", null, false);
when(modelBridge.getNotificationsPreferences(userReference)).thenReturn(Arrays.asList(pref1));
// Test
List<CompositeEvent> results
= mocker.getComponentUnderTest().getEvents("xwiki:XWiki.UserA", true, 2);
// Verify
assertEquals(0, results.size());
}
@Test
public void getEventsWhenException() throws Exception
{
// Mocks
NotificationException exception = new NotificationException("Error");
when(queryGenerator.generateQuery(eq(userReference), eq(true), isNull(), any(List.class))).thenThrow(exception);
// Test
NotificationException caughtException = null;
try {
mocker.getComponentUnderTest().getEvents("xwiki:XWiki.UserA", true, 2);
} catch (NotificationException e) {
caughtException = e;
}
// Verify
assertNotNull(caughtException);
assertEquals("Fail to get the list of notifications.", caughtException.getMessage());
assertEquals(exception, caughtException.getCause());
}
@Test
public void getEventsCount() throws Exception
{
// Mocks
Event event1 = mock(Event.class);
Event event2 = mock(Event.class);
Event event3 = mock(Event.class);
when(eventStream.searchEvents(query)).thenReturn(
Arrays.asList(event1, event2, event1, event2, event2, event2, event1, event2, event2, event2),
Arrays.asList(event1, event2, event2, event1, event3));
// Test
long result = mocker.getComponentUnderTest().getEventsCount("xwiki:XWiki.UserA", true, 5);
// Verify
assertEquals(5, result);
verifyZeroInteractions(event3);
}
@Test
public void getEventsUC1() throws Exception
{
// Facts:
// * Alice updates the page "Bike"
// * Bob updates the page "Bike"
// Expected:
// * Alice and Bob have updated the page "Bike"
// Comment:
// Note: the 2 events have been combined
// Mocks
Event eventAlice = mock(Event.class);
Event eventBob = mock(Event.class);
DocumentReference doc = new DocumentReference("xwiki", "Main", "Bike");
when(eventAlice.getDocument()).thenReturn(doc);
when(eventBob.getDocument()).thenReturn(doc);
when(authorizationManager.hasAccess(Right.VIEW, userReference, doc)).thenReturn(true);
when(eventAlice.getType()).thenReturn("update");
when(eventBob.getType()).thenReturn("update");
when(eventStream.searchEvents(query)).thenReturn(Arrays.asList(eventAlice, eventBob));
// Test
List<CompositeEvent> results
= mocker.getComponentUnderTest().getEvents("xwiki:XWiki.UserA", true, 2);
// Verify
assertEquals(1, results.size());
assertEquals(eventAlice, results.get(0).getEvents().get(0));
assertEquals(eventBob, results.get(0).getEvents().get(1));
}
@Test
public void getEventsUC2() throws Exception
{
// Facts:
// * Bob comments the page "Bike" (which actually update the page too)
// Expected:
// * Bob has commented the page "Bike"
// Comment: we do not mention that Bob has updated the page "Bike", because it's actually a technical
// implementation of the "comment" feature.
// Mocks
Event eventComment = mock(Event.class);
Event eventUpdate = mock(Event.class);
DocumentReference doc = new DocumentReference("xwiki", "Main", "Bike");
when(eventComment.getDocument()).thenReturn(doc);
when(eventUpdate.getDocument()).thenReturn(doc);
when(authorizationManager.hasAccess(Right.VIEW, userReference, doc)).thenReturn(true);
when(eventComment.getType()).thenReturn("addComment");
when(eventUpdate.getType()).thenReturn("update");
when(eventComment.getGroupId()).thenReturn("g1");
when(eventUpdate.getGroupId()).thenReturn("g1");
when(eventStream.searchEvents(query)).thenReturn(Arrays.asList(eventComment, eventUpdate));
// Test
List<CompositeEvent> results
= mocker.getComponentUnderTest().getEvents("xwiki:XWiki.UserA", true, 2);
// Verify
assertEquals(1, results.size());
assertEquals(eventComment, results.get(0).getEvents().get(0));
assertEquals(eventUpdate, results.get(0).getEvents().get(1));
}
@Test
public void getEventsUC3() throws Exception
{
// Facts:
// * Alice updates the page "Bike"
// * Bob comments the page "Bike"
// Expected:
// * Alice has updated the page "Bike"
// * Bob has commented the page "Bike"
// Comment: same as UC2 but we make sure we don't lose the event concerning Alice
// Note: the UC4 described in https://jira.xwiki.org/browse/XWIKI-14114 is actually similar to that one
// because we don't care of the event' user in our tests.
// Mocks
Event event1 = mock(Event.class);
Event event2 = mock(Event.class);
Event event3 = mock(Event.class);
DocumentReference doc = new DocumentReference("xwiki", "Main", "Bike");
when(event1.getDocument()).thenReturn(doc);
when(event2.getDocument()).thenReturn(doc);
when(event3.getDocument()).thenReturn(doc);
when(authorizationManager.hasAccess(Right.VIEW, userReference, doc)).thenReturn(true);
when(event1.getType()).thenReturn("update");
when(event2.getType()).thenReturn("addComment");
when(event3.getType()).thenReturn("update");
when(event1.getGroupId()).thenReturn("g1");
when(event2.getGroupId()).thenReturn("g2");
when(event3.getGroupId()).thenReturn("g2");
when(eventStream.searchEvents(query)).thenReturn(Arrays.asList(event1, event2, event3));
// Test
List<CompositeEvent> results
= mocker.getComponentUnderTest().getEvents("xwiki:XWiki.UserA", true, 5);
// Verify
assertEquals(2, results.size());
assertEquals(event1, results.get(0).getEvents().get(0));
assertEquals(event2, results.get(1).getEvents().get(0));
assertEquals(event3, results.get(1).getEvents().get(1));
}
@Test
public void getEventsUC5() throws Exception
{
// Facts:
// * Bob updates the page "Bike"
// * Then Bob updates the page "Bike" again
// Expected:
// * Bob has updated the page "Bike"
// Comment: we don't show 2 events, only one is interesting
// Mocks
Event event1 = mock(Event.class);
Event event2 = mock(Event.class);
DocumentReference doc = new DocumentReference("xwiki", "Main", "Bike");
when(event1.getDocument()).thenReturn(doc);
when(event2.getDocument()).thenReturn(doc);
when(authorizationManager.hasAccess(Right.VIEW, userReference, doc)).thenReturn(true);
when(event1.getType()).thenReturn("update");
when(event2.getType()).thenReturn("update");
when(event1.getGroupId()).thenReturn("g1");
when(event2.getGroupId()).thenReturn("g2");
when(eventStream.searchEvents(query)).thenReturn(Arrays.asList(event1, event2));
// Test
List<CompositeEvent> results
= mocker.getComponentUnderTest().getEvents("xwiki:XWiki.UserA", true, 5);
// Verify
assertEquals(1, results.size());
assertEquals(event1, results.get(0).getEvents().get(0));
assertEquals(event2, results.get(0).getEvents().get(1));
}
@Test
public void getEventsUC6() throws Exception
{
// Facts:
// * Bob updates the page "Bike" (E1)
// * Alice updates the page "Bike" (E2)
// * Bob comments the page "Bike" (E3 & E4)
// * Carol comments the page "Bike" (E5 & E6)
// * Dave comments the page "Guitar" (E7 & E8)
// * Bob adds an annotation on page "Bike" (E9 & E10)
// * Alice adds an annotation on page "Bike" (E11 & E12)
// * Alice adds an other annotation on page "Bike" (E12 & E13)
// Expected:
// * Bob and Alice have updated the page "Bike"
// * Bob and Carol have commented the page "Bike"
// * Dave has commented the page "Guitar"
// * Bob and Alice have annotated the page "Bike"
// Comment: it's only a mix of other use cases to make sure we have the expected results.
// Mocks
DocumentReference doc1 = new DocumentReference("xwiki", "Main", "Bike");
DocumentReference doc2 = new DocumentReference("xwiki", "Main", "Guitar");
when(authorizationManager.hasAccess(Right.VIEW, userReference, doc1)).thenReturn(true);
when(authorizationManager.hasAccess(Right.VIEW, userReference, doc2)).thenReturn(true);
// * Bob updates the page "Bike" (E1)
Event event1 = mock(Event.class); when(event1.toString()).thenReturn("event1");
when(event1.getDocument()).thenReturn(doc1);
when(event1.getType()).thenReturn("update");
when(event1.getGroupId()).thenReturn("g1");
// * Alice updates the page "Bike" (E2)
Event event2 = mock(Event.class); when(event2.toString()).thenReturn("event2");
when(event2.getDocument()).thenReturn(doc1);
when(event2.getType()).thenReturn("update");
when(event2.getGroupId()).thenReturn("g2");
// * Bob comments the page "Bike" (E3 & E4)
Event event3 = mock(Event.class); when(event3.toString()).thenReturn("event3");
when(event3.getDocument()).thenReturn(doc1);
when(event3.getType()).thenReturn("addComment");
when(event3.getGroupId()).thenReturn("g3");
Event event4 = mock(Event.class); when(event4.toString()).thenReturn("event4");
when(event4.getDocument()).thenReturn(doc1);
when(event4.getType()).thenReturn("update");
when(event4.getGroupId()).thenReturn("g3");
// * Carol comments the page "Bike" (E5 & E6)
// (note: we put the "update" event before the "addComment", because we can not guarantee the order so
// it's good to test both)
Event event5 = mock(Event.class); when(event5.toString()).thenReturn("event5");
when(event5.getDocument()).thenReturn(doc1);
when(event5.getType()).thenReturn("update");
when(event5.getGroupId()).thenReturn("g5");
Event event6 = mock(Event.class); when(event6.toString()).thenReturn("event6");
when(event6.getDocument()).thenReturn(doc1);
when(event6.getType()).thenReturn("addComment");
when(event6.getGroupId()).thenReturn("g5");
// * Dave comments the page "Guitar" (E7 & E8)
Event event7 = mock(Event.class); when(event7.toString()).thenReturn("event7");
when(event7.getDocument()).thenReturn(doc2);
when(event7.getType()).thenReturn("update");
when(event7.getGroupId()).thenReturn("g7");
Event event8 = mock(Event.class); when(event8.toString()).thenReturn("event8");
when(event8.getDocument()).thenReturn(doc2);
when(event8.getType()).thenReturn("addComment");
when(event8.getGroupId()).thenReturn("g7");
// * Bob adds an annotation on page "Bike" (E9 & E10)
Event event9 = mock(Event.class); when(event8.toString()).thenReturn("event9");
when(event9.getDocument()).thenReturn(doc1);
when(event9.getType()).thenReturn("update");
when(event9.getGroupId()).thenReturn("g9");
Event event10 = mock(Event.class); when(event8.toString()).thenReturn("event10");
when(event10.getDocument()).thenReturn(doc1);
when(event10.getType()).thenReturn("addAnnotation");
when(event10.getGroupId()).thenReturn("g9");
// * Alice adds an annotation on page "Bike" (E11 & E12)
Event event11 = mock(Event.class); when(event8.toString()).thenReturn("event11");
when(event11.getDocument()).thenReturn(doc1);
when(event11.getType()).thenReturn("update");
when(event11.getGroupId()).thenReturn("g11");
Event event12 = mock(Event.class); when(event8.toString()).thenReturn("event12");
when(event12.getDocument()).thenReturn(doc1);
when(event12.getType()).thenReturn("addAnnotation");
when(event12.getGroupId()).thenReturn("g11");
// * Alice adds an other annotation on page "Bike" (E12 & E13)
Event event13 = mock(Event.class); when(event8.toString()).thenReturn("event11");
when(event13.getDocument()).thenReturn(doc1);
when(event13.getType()).thenReturn("addAnnotation");
when(event13.getGroupId()).thenReturn("g13");
Event event14 = mock(Event.class); when(event8.toString()).thenReturn("event12");
when(event14.getDocument()).thenReturn(doc1);
when(event14.getType()).thenReturn("update");
when(event14.getGroupId()).thenReturn("g13");
when(eventStream.searchEvents(query)).thenReturn(Arrays.asList(event1, event2, event3, event4, event5, event6,
event7, event8, event9, event10, event11, event12, event13, event14));
// Test
List<CompositeEvent> results
= mocker.getComponentUnderTest().getEvents("xwiki:XWiki.UserA", true, 50);
// Verify
assertEquals(4, results.size());
// * Bob and Alice have updated the page "Bike"
assertTrue(results.get(0).getEvents().contains(event1));
assertTrue(results.get(0).getEvents().contains(event2));
// * Bob and Carol have commented the page "Bike"
assertTrue(results.get(1).getEvents().contains(event3));
assertTrue(results.get(1).getEvents().contains(event4));
assertTrue(results.get(1).getEvents().contains(event5));
assertTrue(results.get(1).getEvents().contains(event6));
// * Dave has commented the page "Guitar"
assertTrue(results.get(2).getEvents().contains(event7));
assertTrue(results.get(2).getEvents().contains(event8));
// * Bob and Alice have annotated the page "Bike"
assertTrue(results.get(3).getEvents().contains(event9));
assertTrue(results.get(3).getEvents().contains(event10));
assertTrue(results.get(3).getEvents().contains(event11));
assertTrue(results.get(3).getEvents().contains(event12));
assertTrue(results.get(3).getEvents().contains(event13));
assertTrue(results.get(3).getEvents().contains(event14));
}
@Test
public void getEvents1Update2Events() throws Exception
{
// Facts:
// * Bob comment and annotate the page "Bike" in the same time
// Expected:
// * Bob has commented the page "Bike"
// * Bob has annotated the page "Bike"
// Mocks
Event event1 = mock(Event.class); when(event1.toString()).thenReturn("event1");
Event event2 = mock(Event.class); when(event1.toString()).thenReturn("event2");
Event event3 = mock(Event.class); when(event1.toString()).thenReturn("event3");
DocumentReference doc = new DocumentReference("xwiki", "Main", "Bike");
when(event1.getDocument()).thenReturn(doc);
when(event2.getDocument()).thenReturn(doc);
when(event3.getDocument()).thenReturn(doc);
when(authorizationManager.hasAccess(Right.VIEW, userReference, doc)).thenReturn(true);
when(event1.getType()).thenReturn("update");
when(event2.getType()).thenReturn("addComment");
when(event3.getType()).thenReturn("addAnnotation");
when(event1.getGroupId()).thenReturn("g1");
when(event2.getGroupId()).thenReturn("g1");
when(event3.getGroupId()).thenReturn("g1");
when(eventStream.searchEvents(query)).thenReturn(Arrays.asList(event1, event2, event3));
// Test
List<CompositeEvent> results
= mocker.getComponentUnderTest().getEvents("xwiki:XWiki.UserA", true, 50);
// Verify
assertEquals(1, results.size());
assertTrue(results.get(0).getEvents().contains(event1));
assertTrue(results.get(0).getEvents().contains(event2));
assertTrue(results.get(0).getEvents().contains(event3));
}
}