You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2019/01/11 09:58:19 UTC

[6/8] james-project git commit: MAILBOX-368 Separating Group and Key Contract from EventBusContract

MAILBOX-368 Separating Group and Key Contract from EventBusContract


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/bca9ba01
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/bca9ba01
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/bca9ba01

Branch: refs/heads/master
Commit: bca9ba01b8d52562844c14653f9196a617cc1ae9
Parents: 92842f4
Author: datph <dp...@linagora.com>
Authored: Tue Jan 8 14:03:35 2019 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Fri Jan 11 16:57:08 2019 +0700

----------------------------------------------------------------------
 .../james/mailbox/events/EventBusContract.java  | 359 -------------------
 .../mailbox/events/EventBusTestFixture.java     |  65 ++++
 .../events/EventDeadLettersContract.java        |   4 +-
 .../james/mailbox/events/GroupContract.java     | 167 +++++++++
 .../james/mailbox/events/KeyContract.java       | 246 +++++++++++++
 .../james/mailbox/events/InVMEventBusTest.java  |   2 +-
 6 files changed, 481 insertions(+), 362 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/bca9ba01/mailbox/api/src/test/java/org/apache/james/mailbox/events/EventBusContract.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/events/EventBusContract.java b/mailbox/api/src/test/java/org/apache/james/mailbox/events/EventBusContract.java
index ab54208..50d8d67 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/events/EventBusContract.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/events/EventBusContract.java
@@ -19,366 +19,7 @@
 
 package org.apache.james.mailbox.events;
 
-import static org.assertj.core.api.Assertions.assertThatCode;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.junit.jupiter.api.Assertions.assertTimeout;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.after;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import java.time.Duration;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-
-import org.apache.james.core.User;
-import org.apache.james.mailbox.Event;
-import org.apache.james.mailbox.MailboxListener;
-import org.apache.james.mailbox.MailboxSession;
-import org.apache.james.mailbox.model.MailboxConstants;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.mailbox.model.TestId;
-import org.junit.jupiter.api.Test;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSortedMap;
-
 public interface EventBusContract {
-    MailboxListener.MailboxEvent EVENT = new MailboxListener.MailboxAdded(
-        MailboxSession.SessionId.of(42),
-        User.fromUsername("user"),
-        new MailboxPath(MailboxConstants.USER_NAMESPACE, "user", "mailboxName"),
-        TestId.of(18),
-        Event.EventId.random());
-    MailboxListener.Added NOOP_EVENT = new MailboxListener.Added(MailboxSession.SessionId.of(18), User.fromUsername("bob"), MailboxPath.forUser("bob", "mailbox"), TestId.of(58), ImmutableSortedMap.of(), Event.EventId.random());
-
-    class GroupA extends Group {}
-
-    class GroupB extends Group {}
-
-    int ONE_SECOND = 1000;
-    int FIVE_HUNDRED_MS = 500;
-    MailboxId ID_1 = TestId.of(18);
-    MailboxId ID_2 = TestId.of(24);
-    ImmutableSet<RegistrationKey> NO_KEYS = ImmutableSet.of();
-    MailboxIdRegistrationKey KEY_1 = new MailboxIdRegistrationKey(ID_1);
-    MailboxIdRegistrationKey KEY_2 = new MailboxIdRegistrationKey(ID_2);
-    List<Class<? extends Group>> ALL_GROUPS = ImmutableList.of(GroupA.class, GroupB.class);
 
     EventBus eventBus();
-
-    default MailboxListener newListener() {
-        MailboxListener listener = mock(MailboxListener.class);
-        when(listener.getExecutionMode()).thenReturn(MailboxListener.ExecutionMode.SYNCHRONOUS);
-        return listener;
-    }
-
-    @Test
-    default void listenerGroupShouldReceiveEvents() {
-        MailboxListener listener = newListener();
-
-        eventBus().register(listener, new GroupA());
-
-        eventBus().dispatch(EVENT, NO_KEYS).block();
-
-        verify(listener, timeout(ONE_SECOND).times(1)).event(any());
-    }
-
-    @Test
-    default void groupListenersShouldNotReceiveNoopEvents() {
-        MailboxListener listener = newListener();
-
-        eventBus().register(listener, new GroupA());
-
-        eventBus().dispatch(NOOP_EVENT, NO_KEYS).block();
-
-        verifyNoMoreInteractions(listener);
-    }
-
-    @Test
-    default void registeredListenersShouldNotReceiveNoopEvents() {
-        MailboxListener listener = newListener();
-
-        eventBus().register(listener, KEY_1);
-
-        eventBus().dispatch(NOOP_EVENT, KEY_1).block();
-
-        verifyNoMoreInteractions(listener);
-    }
-
-    @Test
-    default void dispatchShouldNotThrowWhenAGroupListenerFails() {
-        MailboxListener listener = newListener();
-        doThrow(new RuntimeException()).when(listener).event(any());
-
-        eventBus().register(listener, new GroupA());
-
-        assertThatCode(() -> eventBus().dispatch(EVENT, NO_KEYS).block())
-            .doesNotThrowAnyException();
-    }
-
-    @Test
-    default void dispatchShouldNotThrowWhenARegisteredListenerFails() {
-        MailboxListener listener = newListener();
-        doThrow(new RuntimeException()).when(listener).event(any());
-
-        eventBus().register(listener, KEY_1);
-
-        assertThatCode(() -> eventBus().dispatch(EVENT, NO_KEYS).block())
-            .doesNotThrowAnyException();
-    }
-
-    @Test
-    default void eachListenerGroupShouldReceiveEvents() {
-        MailboxListener listener = newListener();
-        MailboxListener listener2 = newListener();
-        eventBus().register(listener, new GroupA());
-        eventBus().register(listener2, new GroupB());
-
-        eventBus().dispatch(EVENT, NO_KEYS).block();
-
-        verify(listener, timeout(ONE_SECOND).times(1)).event(any());
-        verify(listener2, timeout(ONE_SECOND).times(1)).event(any());
-    }
-
-    @Test
-    default void unregisteredGroupListenerShouldNotReceiveEvents() {
-        MailboxListener listener = newListener();
-        Registration registration = eventBus().register(listener, new GroupA());
-
-        registration.unregister();
-
-        eventBus().dispatch(EVENT, NO_KEYS).block();
-        verify(listener, after(FIVE_HUNDRED_MS).never())
-            .event(any());
-    }
-
-    @Test
-    default void registerShouldThrowWhenAGroupIsAlreadyUsed() {
-        MailboxListener listener = newListener();
-        MailboxListener listener2 = newListener();
-
-        eventBus().register(listener, new GroupA());
-
-        assertThatThrownBy(() -> eventBus().register(listener2, new GroupA()))
-            .isInstanceOf(GroupAlreadyRegistered.class);
-    }
-
-    @Test
-    default void registerShouldNotThrowOnAnUnregisteredGroup() {
-        MailboxListener listener = newListener();
-        MailboxListener listener2 = newListener();
-
-        eventBus().register(listener, new GroupA()).unregister();
-
-        assertThatCode(() -> eventBus().register(listener2, new GroupA()))
-            .doesNotThrowAnyException();
-    }
-
-    @Test
-    default void unregisterShouldBeIdempotentForGroups() {
-        MailboxListener listener = newListener();
-
-        Registration registration = eventBus().register(listener, new GroupA());
-        registration.unregister();
-
-        assertThatCode(registration::unregister)
-            .doesNotThrowAnyException();
-    }
-
-    @Test
-    default void registerShouldAcceptAlreadyUnregisteredGroups() {
-        MailboxListener listener = newListener();
-
-        eventBus().register(listener, new GroupA()).unregister();
-        eventBus().register(listener, new GroupA());
-
-        eventBus().dispatch(EVENT, NO_KEYS).block();
-
-        verify(listener, timeout(ONE_SECOND).times(1)).event(any());
-    }
-
-    @Test
-    default void dispatchShouldNotNotifyRegisteredListenerWhenEmptyKeySet() {
-        MailboxListener listener = newListener();
-        eventBus().register(listener, KEY_1);
-
-        eventBus().dispatch(EVENT, NO_KEYS).block();
-
-        verify(listener, after(FIVE_HUNDRED_MS).never())
-            .event(any());
-    }
-
-    @Test
-    default void dispatchShouldNotNotifyListenerRegisteredOnOtherKeys() {
-        MailboxListener listener = newListener();
-        eventBus().register(listener, KEY_1);
-
-        eventBus().dispatch(EVENT, ImmutableSet.of(KEY_2)).block();
-
-        verify(listener, after(FIVE_HUNDRED_MS).never())
-            .event(any());
-    }
-
-    @Test
-    default void dispatchShouldNotifyRegisteredListeners() {
-        MailboxListener listener = newListener();
-        eventBus().register(listener, KEY_1);
-
-        eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1)).block();
-
-        verify(listener, timeout(ONE_SECOND).times(1)).event(any());
-    }
-
-    @Test
-    default void dispatchShouldNotifyOnlyRegisteredListener() {
-        MailboxListener listener = newListener();
-        MailboxListener listener2 = newListener();
-        eventBus().register(listener, KEY_1);
-        eventBus().register(listener2, KEY_2);
-
-        eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1)).block();
-
-        verify(listener, timeout(ONE_SECOND).times(1)).event(any());
-        verify(listener2, after(FIVE_HUNDRED_MS).never())
-            .event(any());
-    }
-
-    @Test
-    default void dispatchShouldNotifyAllListenersRegisteredOnAKey() {
-        MailboxListener listener = newListener();
-        MailboxListener listener2 = newListener();
-        eventBus().register(listener, KEY_1);
-        eventBus().register(listener2, KEY_1);
-
-        eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1)).block();
-
-        verify(listener, timeout(ONE_SECOND).times(1)).event(any());
-        verify(listener2, timeout(ONE_SECOND).times(1)).event(any());
-    }
-
-    @Test
-    default void registerShouldAllowDuplicatedRegistration() {
-        MailboxListener listener = newListener();
-        eventBus().register(listener, KEY_1);
-        eventBus().register(listener, KEY_1);
-
-        eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1)).block();
-
-        verify(listener, timeout(ONE_SECOND).times(1)).event(any());
-    }
-
-    @Test
-    default void unregisterShouldRemoveDoubleRegisteredListener() {
-        MailboxListener listener = newListener();
-        eventBus().register(listener, KEY_1);
-        eventBus().register(listener, KEY_1).unregister();
-
-        eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1)).block();
-
-        verify(listener, after(FIVE_HUNDRED_MS).never())
-            .event(any());
-    }
-
-    @Test
-    default void callingAllUnregisterMethodShouldUnregisterTheListener() {
-        MailboxListener listener = newListener();
-        Registration registration = eventBus().register(listener, KEY_1);
-        eventBus().register(listener, KEY_1).unregister();
-        registration.unregister();
-
-        eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1)).block();
-
-        verify(listener, after(FIVE_HUNDRED_MS).never())
-            .event(any());
-    }
-
-    @Test
-    default void unregisterShouldHaveNotNotifyWhenCalledOnDifferentKeys() {
-        MailboxListener listener = newListener();
-        eventBus().register(listener, KEY_1);
-        eventBus().register(listener, KEY_2).unregister();
-
-        eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1)).block();
-
-        verify(listener, timeout(ONE_SECOND).times(1)).event(any());
-    }
-
-    @Test
-    default void unregisterShouldBeIdempotentForKeyRegistrations() {
-        MailboxListener listener = newListener();
-
-        Registration registration = eventBus().register(listener, KEY_1);
-        registration.unregister();
-
-        assertThatCode(registration::unregister)
-            .doesNotThrowAnyException();
-    }
-
-    @Test
-    default void dispatchShouldAcceptSeveralKeys() {
-        MailboxListener listener = newListener();
-        eventBus().register(listener, KEY_1);
-
-        eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1, KEY_2)).block();
-
-        verify(listener, timeout(ONE_SECOND).times(1)).event(any());
-    }
-
-    @Test
-    default void dispatchShouldCallListenerOnceWhenSeveralKeysMatching() {
-        MailboxListener listener = newListener();
-        eventBus().register(listener, KEY_1);
-        eventBus().register(listener, KEY_2);
-
-        eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1, KEY_2)).block();
-
-        verify(listener, timeout(ONE_SECOND).times(1)).event(any());
-    }
-
-    @Test
-    default void dispatchShouldNotNotifyUnregisteredListener() {
-        MailboxListener listener = newListener();
-        eventBus().register(listener, KEY_1).unregister();
-
-        eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1)).block();
-
-        verify(listener, after(FIVE_HUNDRED_MS).never())
-            .event(any());
-    }
-
-    @Test
-    default void dispatchShouldCallSynchronousListener() {
-        MailboxListener listener = newListener();
-
-        eventBus().register(listener, new GroupA());
-
-        eventBus().dispatch(EVENT, NO_KEYS).block();
-
-        verify(listener, timeout(ONE_SECOND).times(1)).event(any());
-    }
-
-    @Test
-    default void dispatchShouldNotBlockAsynchronousListener() {
-        MailboxListener listener = newListener();
-        when(listener.getExecutionMode()).thenReturn(MailboxListener.ExecutionMode.ASYNCHRONOUS);
-        CountDownLatch latch = new CountDownLatch(1);
-        doAnswer(invocation -> {
-            latch.await();
-            return null;
-        }).when(listener).event(EVENT);
-
-        assertTimeout(Duration.ofSeconds(2),
-            () -> {
-                eventBus().dispatch(EVENT, NO_KEYS).block();
-                latch.countDown();
-            });
-    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/bca9ba01/mailbox/api/src/test/java/org/apache/james/mailbox/events/EventBusTestFixture.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/events/EventBusTestFixture.java b/mailbox/api/src/test/java/org/apache/james/mailbox/events/EventBusTestFixture.java
new file mode 100644
index 0000000..ae2116d
--- /dev/null
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/events/EventBusTestFixture.java
@@ -0,0 +1,65 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you 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 org.apache.james.mailbox.events;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+
+import org.apache.james.core.User;
+import org.apache.james.mailbox.Event;
+import org.apache.james.mailbox.MailboxListener;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.model.MailboxConstants;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.TestId;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+public interface EventBusTestFixture {
+
+    class GroupA extends Group {}
+    class GroupB extends Group {}
+
+    MailboxListener.MailboxEvent EVENT = new MailboxListener.MailboxAdded(
+        MailboxSession.SessionId.of(42),
+        User.fromUsername("user"),
+        new MailboxPath(MailboxConstants.USER_NAMESPACE, "user", "mailboxName"),
+        TestId.of(18),
+        Event.EventId.random());
+
+    int ONE_SECOND = 1000;
+    int FIVE_HUNDRED_MS = 500;
+    MailboxId ID_1 = TestId.of(18);
+    MailboxId ID_2 = TestId.of(24);
+    ImmutableSet<RegistrationKey> NO_KEYS = ImmutableSet.of();
+    MailboxIdRegistrationKey KEY_1 = new MailboxIdRegistrationKey(ID_1);
+    MailboxIdRegistrationKey KEY_2 = new MailboxIdRegistrationKey(ID_2);
+    List<Class<? extends Group>> ALL_GROUPS = ImmutableList.of(GroupA.class, GroupB.class);
+
+    static MailboxListener newListener() {
+        MailboxListener listener = mock(MailboxListener.class);
+        when(listener.getExecutionMode()).thenReturn(MailboxListener.ExecutionMode.SYNCHRONOUS);
+        return listener;
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/bca9ba01/mailbox/api/src/test/java/org/apache/james/mailbox/events/EventDeadLettersContract.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/events/EventDeadLettersContract.java b/mailbox/api/src/test/java/org/apache/james/mailbox/events/EventDeadLettersContract.java
index d70395a..e2282a1 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/events/EventDeadLettersContract.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/events/EventDeadLettersContract.java
@@ -86,8 +86,8 @@ interface EventDeadLettersContract {
     MailboxListener.MailboxAdded EVENT_2 = new MailboxListener.MailboxAdded(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, EVENT_ID_2);
     MailboxListener.MailboxAdded EVENT_3 = new MailboxListener.MailboxAdded(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, EVENT_ID_3);
 
-    Group GROUP_A = new EventBusContract.GroupA();
-    Group GROUP_B = new EventBusContract.GroupB();
+    Group GROUP_A = new EventBusTestFixture.GroupA();
+    Group GROUP_B = new EventBusTestFixture.GroupB();
     Group NULL_GROUP = null;
     Event NULL_EVENT = null;
     Event.EventId NULL_EVENT_ID = null;

http://git-wip-us.apache.org/repos/asf/james-project/blob/bca9ba01/mailbox/api/src/test/java/org/apache/james/mailbox/events/GroupContract.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/events/GroupContract.java b/mailbox/api/src/test/java/org/apache/james/mailbox/events/GroupContract.java
new file mode 100644
index 0000000..0975e3b
--- /dev/null
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/events/GroupContract.java
@@ -0,0 +1,167 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you 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 org.apache.james.mailbox.events;
+
+import static org.apache.james.mailbox.events.EventBusTestFixture.EVENT;
+import static org.apache.james.mailbox.events.EventBusTestFixture.FIVE_HUNDRED_MS;
+import static org.apache.james.mailbox.events.EventBusTestFixture.GroupA;
+import static org.apache.james.mailbox.events.EventBusTestFixture.GroupB;
+import static org.apache.james.mailbox.events.EventBusTestFixture.NO_KEYS;
+import static org.apache.james.mailbox.events.EventBusTestFixture.ONE_SECOND;
+import static org.apache.james.mailbox.events.EventBusTestFixture.newListener;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import org.apache.james.core.User;
+import org.apache.james.mailbox.Event;
+import org.apache.james.mailbox.MailboxListener;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.TestId;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.collect.ImmutableSortedMap;
+
+public interface GroupContract {
+
+    interface SingleEventBusGroupContract extends EventBusContract {
+
+        @Test
+        default void listenerGroupShouldReceiveEvents() {
+            MailboxListener listener = newListener();
+
+            eventBus().register(listener, new GroupA());
+
+            eventBus().dispatch(EVENT, NO_KEYS).block();
+
+            verify(listener, timeout(ONE_SECOND).times(1)).event(any());
+        }
+
+        @Test
+        default void groupListenersShouldNotReceiveNoopEvents() {
+            MailboxListener listener = newListener();
+
+            eventBus().register(listener, new GroupA());
+
+            MailboxListener.Added noopEvent = new MailboxListener.Added(MailboxSession.SessionId.of(18), User.fromUsername("bob"), MailboxPath.forUser("bob", "mailbox"), TestId.of(58), ImmutableSortedMap.of(), Event.EventId.random());
+            eventBus().dispatch(noopEvent, NO_KEYS).block();
+
+            verify(listener, after(FIVE_HUNDRED_MS).never())
+                .event(any());
+        }
+
+        @Test
+        default void dispatchShouldNotThrowWhenAGroupListenerFails() {
+            MailboxListener listener = newListener();
+            doThrow(new RuntimeException()).when(listener).event(any());
+
+            eventBus().register(listener, new GroupA());
+
+            assertThatCode(() -> eventBus().dispatch(EVENT, NO_KEYS).block())
+                .doesNotThrowAnyException();
+        }
+
+        @Test
+        default void eachListenerGroupShouldReceiveEvents() {
+            MailboxListener listener = newListener();
+            MailboxListener listener2 = newListener();
+            eventBus().register(listener, new GroupA());
+            eventBus().register(listener2, new GroupB());
+
+            eventBus().dispatch(EVENT, NO_KEYS).block();
+
+            verify(listener, timeout(ONE_SECOND).times(1)).event(any());
+            verify(listener2, timeout(ONE_SECOND).times(1)).event(any());
+        }
+
+        @Test
+        default void unregisteredGroupListenerShouldNotReceiveEvents() {
+            MailboxListener listener = newListener();
+            Registration registration = eventBus().register(listener, new GroupA());
+
+            registration.unregister();
+
+            eventBus().dispatch(EVENT, NO_KEYS).block();
+            verify(listener, after(FIVE_HUNDRED_MS).never())
+                .event(any());
+        }
+
+        @Test
+        default void registerShouldThrowWhenAGroupIsAlreadyUsed() {
+            MailboxListener listener = newListener();
+            MailboxListener listener2 = newListener();
+
+            eventBus().register(listener, new GroupA());
+
+            assertThatThrownBy(() -> eventBus().register(listener2, new GroupA()))
+                .isInstanceOf(GroupAlreadyRegistered.class);
+        }
+
+        @Test
+        default void registerShouldNotThrowOnAnUnregisteredGroup() {
+            MailboxListener listener = newListener();
+            MailboxListener listener2 = newListener();
+
+            eventBus().register(listener, new GroupA()).unregister();
+
+            assertThatCode(() -> eventBus().register(listener2, new GroupA()))
+                .doesNotThrowAnyException();
+        }
+
+        @Test
+        default void unregisterShouldBeIdempotentForGroups() {
+            MailboxListener listener = newListener();
+
+            Registration registration = eventBus().register(listener, new GroupA());
+            registration.unregister();
+
+            assertThatCode(registration::unregister)
+                .doesNotThrowAnyException();
+        }
+
+        @Test
+        default void registerShouldAcceptAlreadyUnregisteredGroups() {
+            MailboxListener listener = newListener();
+
+            eventBus().register(listener, new GroupA()).unregister();
+            eventBus().register(listener, new GroupA());
+
+            eventBus().dispatch(EVENT, NO_KEYS).block();
+
+            verify(listener, timeout(ONE_SECOND).times(1)).event(any());
+        }
+
+        @Test
+        default void dispatchShouldCallSynchronousListener() {
+            MailboxListener listener = newListener();
+
+            eventBus().register(listener, new GroupA());
+
+            eventBus().dispatch(EVENT, NO_KEYS).block();
+
+            verify(listener, timeout(ONE_SECOND).times(1)).event(any());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/bca9ba01/mailbox/api/src/test/java/org/apache/james/mailbox/events/KeyContract.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/events/KeyContract.java b/mailbox/api/src/test/java/org/apache/james/mailbox/events/KeyContract.java
new file mode 100644
index 0000000..bd92d82
--- /dev/null
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/events/KeyContract.java
@@ -0,0 +1,246 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you 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 org.apache.james.mailbox.events;
+
+import static org.apache.james.mailbox.events.EventBusTestFixture.EVENT;
+import static org.apache.james.mailbox.events.EventBusTestFixture.FIVE_HUNDRED_MS;
+import static org.apache.james.mailbox.events.EventBusTestFixture.KEY_1;
+import static org.apache.james.mailbox.events.EventBusTestFixture.KEY_2;
+import static org.apache.james.mailbox.events.EventBusTestFixture.NO_KEYS;
+import static org.apache.james.mailbox.events.EventBusTestFixture.ONE_SECOND;
+import static org.apache.james.mailbox.events.EventBusTestFixture.newListener;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.junit.jupiter.api.Assertions.assertTimeout;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+
+import org.apache.james.core.User;
+import org.apache.james.mailbox.Event;
+import org.apache.james.mailbox.MailboxListener;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.TestId;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedMap;
+
+public interface KeyContract extends EventBusContract {
+
+    interface SingleEventBusKeyContract extends EventBusContract {
+        @Test
+        default void registeredListenersShouldNotReceiveNoopEvents() {
+            MailboxListener listener = newListener();
+
+            eventBus().register(listener, KEY_1);
+
+            MailboxListener.Added noopEvent = new MailboxListener.Added(MailboxSession.SessionId.of(18), User.fromUsername("bob"), MailboxPath.forUser("bob", "mailbox"), TestId.of(58), ImmutableSortedMap.of(), Event.EventId.random());
+            eventBus().dispatch(noopEvent, KEY_1).block();
+
+            verify(listener, after(FIVE_HUNDRED_MS).never())
+                .event(any());
+        }
+
+        @Test
+        default void dispatchShouldNotThrowWhenARegisteredListenerFails() {
+            MailboxListener listener = newListener();
+            doThrow(new RuntimeException()).when(listener).event(any());
+
+            eventBus().register(listener, KEY_1);
+
+            assertThatCode(() -> eventBus().dispatch(EVENT, NO_KEYS).block())
+                .doesNotThrowAnyException();
+        }
+
+        @Test
+        default void dispatchShouldNotNotifyRegisteredListenerWhenEmptyKeySet() {
+            MailboxListener listener = newListener();
+            eventBus().register(listener, KEY_1);
+
+            eventBus().dispatch(EVENT, NO_KEYS).block();
+
+            verify(listener, after(FIVE_HUNDRED_MS).never())
+                .event(any());
+        }
+
+        @Test
+        default void dispatchShouldNotNotifyListenerRegisteredOnOtherKeys() {
+            MailboxListener listener = newListener();
+            eventBus().register(listener, KEY_1);
+
+            eventBus().dispatch(EVENT, ImmutableSet.of(KEY_2)).block();
+
+            verify(listener, after(FIVE_HUNDRED_MS).never())
+                .event(any());
+        }
+
+        @Test
+        default void dispatchShouldNotifyRegisteredListeners() {
+            MailboxListener listener = newListener();
+            eventBus().register(listener, KEY_1);
+
+            eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1)).block();
+
+            verify(listener, timeout(ONE_SECOND).times(1)).event(any());
+        }
+
+        @Test
+        default void dispatchShouldNotifyOnlyRegisteredListener() {
+            MailboxListener listener = newListener();
+            MailboxListener listener2 = newListener();
+            eventBus().register(listener, KEY_1);
+            eventBus().register(listener2, KEY_2);
+
+            eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1)).block();
+
+            verify(listener, timeout(ONE_SECOND).times(1)).event(any());
+            verify(listener2, after(FIVE_HUNDRED_MS).never())
+                .event(any());
+        }
+
+        @Test
+        default void dispatchShouldNotifyAllListenersRegisteredOnAKey() {
+            MailboxListener listener = newListener();
+            MailboxListener listener2 = newListener();
+            eventBus().register(listener, KEY_1);
+            eventBus().register(listener2, KEY_1);
+
+            eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1)).block();
+
+            verify(listener, timeout(ONE_SECOND).times(1)).event(any());
+            verify(listener2, timeout(ONE_SECOND).times(1)).event(any());
+        }
+
+        @Test
+        default void registerShouldAllowDuplicatedRegistration() {
+            MailboxListener listener = newListener();
+            eventBus().register(listener, KEY_1);
+            eventBus().register(listener, KEY_1);
+
+            eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1)).block();
+
+            verify(listener, timeout(ONE_SECOND).times(1)).event(any());
+        }
+
+        @Test
+        default void unregisterShouldRemoveDoubleRegisteredListener() {
+            MailboxListener listener = newListener();
+            eventBus().register(listener, KEY_1);
+            eventBus().register(listener, KEY_1).unregister();
+
+            eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1)).block();
+
+            verify(listener, after(FIVE_HUNDRED_MS).never())
+                .event(any());
+        }
+
+        @Test
+        default void callingAllUnregisterMethodShouldUnregisterTheListener() {
+            MailboxListener listener = newListener();
+            Registration registration = eventBus().register(listener, KEY_1);
+            eventBus().register(listener, KEY_1).unregister();
+            registration.unregister();
+
+            eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1)).block();
+
+            verify(listener, after(FIVE_HUNDRED_MS).never())
+                .event(any());
+        }
+
+        @Test
+        default void unregisterShouldHaveNotNotifyWhenCalledOnDifferentKeys() {
+            MailboxListener listener = newListener();
+            eventBus().register(listener, KEY_1);
+            eventBus().register(listener, KEY_2).unregister();
+
+            eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1)).block();
+
+            verify(listener, timeout(ONE_SECOND).times(1)).event(any());
+        }
+
+        @Test
+        default void unregisterShouldBeIdempotentForKeyRegistrations() {
+            MailboxListener listener = newListener();
+
+            Registration registration = eventBus().register(listener, KEY_1);
+            registration.unregister();
+
+            assertThatCode(registration::unregister)
+                .doesNotThrowAnyException();
+        }
+
+        @Test
+        default void dispatchShouldAcceptSeveralKeys() {
+            MailboxListener listener = newListener();
+            eventBus().register(listener, KEY_1);
+
+            eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1, KEY_2)).block();
+
+            verify(listener, timeout(ONE_SECOND).times(1)).event(any());
+        }
+
+        @Test
+        default void dispatchShouldCallListenerOnceWhenSeveralKeysMatching() {
+            MailboxListener listener = newListener();
+            eventBus().register(listener, KEY_1);
+            eventBus().register(listener, KEY_2);
+
+            eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1, KEY_2)).block();
+
+            verify(listener, timeout(ONE_SECOND).times(1)).event(any());
+        }
+
+        @Test
+        default void dispatchShouldNotNotifyUnregisteredListener() {
+            MailboxListener listener = newListener();
+            eventBus().register(listener, KEY_1).unregister();
+
+            eventBus().dispatch(EVENT, ImmutableSet.of(KEY_1)).block();
+
+            verify(listener, after(FIVE_HUNDRED_MS).never())
+                .event(any());
+        }
+
+        @Test
+        default void dispatchShouldNotBlockAsynchronousListener() {
+            MailboxListener listener = newListener();
+            when(listener.getExecutionMode()).thenReturn(MailboxListener.ExecutionMode.ASYNCHRONOUS);
+            CountDownLatch latch = new CountDownLatch(1);
+            doAnswer(invocation -> {
+                latch.await();
+                return null;
+            }).when(listener).event(EVENT);
+
+            assertTimeout(Duration.ofSeconds(2),
+                () -> {
+                    eventBus().dispatch(EVENT, NO_KEYS).block();
+                    latch.countDown();
+                });
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/bca9ba01/mailbox/event/event-memory/src/test/java/org/apache/james/mailbox/events/InVMEventBusTest.java
----------------------------------------------------------------------
diff --git a/mailbox/event/event-memory/src/test/java/org/apache/james/mailbox/events/InVMEventBusTest.java b/mailbox/event/event-memory/src/test/java/org/apache/james/mailbox/events/InVMEventBusTest.java
index 6d278aa..0241ac7 100644
--- a/mailbox/event/event-memory/src/test/java/org/apache/james/mailbox/events/InVMEventBusTest.java
+++ b/mailbox/event/event-memory/src/test/java/org/apache/james/mailbox/events/InVMEventBusTest.java
@@ -23,7 +23,7 @@ import org.apache.james.mailbox.events.delivery.InVmEventDelivery;
 import org.apache.james.metrics.api.NoopMetricFactory;
 import org.junit.jupiter.api.BeforeEach;
 
-public class InVMEventBusTest implements EventBusContract {
+public class InVMEventBusTest implements KeyContract.SingleEventBusKeyContract, GroupContract.SingleEventBusGroupContract {
     private InVMEventBus eventBus;
 
     @BeforeEach


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org