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 2018/12/19 11:24:45 UTC

[04/10] james-project git commit: MAILBOX-359 Expunged Scala Event Serialization

MAILBOX-359 Expunged Scala Event Serialization

Added and Expunged have 100% same structure, so nothing different except
class name and Json Object name! Event Serialization and tests for Expunged is
duplicated from Added


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

Branch: refs/heads/master
Commit: 9e5b1bf1516908c0aa57b439bc0cd5b42ebf3617
Parents: 39bc507
Author: tran tien duc <dt...@linagora.com>
Authored: Mon Dec 17 11:17:12 2018 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Wed Dec 19 18:05:58 2018 +0700

----------------------------------------------------------------------
 .../apache/james/mailbox/MailboxListener.java   |   31 +-
 .../james/mailbox/MailboxListenerTest.java      |    5 +
 .../james/event/json/EventSerializer.scala      |   25 +-
 .../event/json/ExpungedSerializationTest.java   | 1465 ++++++++++++++++++
 4 files changed, 1519 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/9e5b1bf1/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
index 2b7fdb9..60d8aa2 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java
@@ -376,16 +376,16 @@ public interface MailboxListener {
     }
 
     class Expunged extends MetaDataHoldingEvent {
-        private final Map<MessageUid, MessageMetaData> uids;
+        private final Map<MessageUid, MessageMetaData> expunged;
 
         public Expunged(MailboxSession.SessionId sessionId, User user, MailboxPath path, MailboxId mailboxId, Map<MessageUid, MessageMetaData> uids) {
             super(sessionId, user, path, mailboxId);
-            this.uids = ImmutableMap.copyOf(uids);
+            this.expunged = ImmutableMap.copyOf(uids);
         }
 
         @Override
         public Collection<MessageUid> getUids() {
-            return uids.keySet();
+            return expunged.keySet();
         }
 
         /**
@@ -395,7 +395,30 @@ public interface MailboxListener {
          */
         @Override
         public MessageMetaData getMetaData(MessageUid uid) {
-            return uids.get(uid);
+            return expunged.get(uid);
+        }
+
+        public Map<MessageUid, MessageMetaData> getExpunged() {
+            return expunged;
+        }
+
+        @Override
+        public final boolean equals(Object o) {
+            if (o instanceof Expunged) {
+                Expunged that = (Expunged) o;
+
+                return Objects.equals(this.sessionId, that.sessionId)
+                    && Objects.equals(this.user, that.user)
+                    && Objects.equals(this.path, that.path)
+                    && Objects.equals(this.mailboxId, that.mailboxId)
+                    && Objects.equals(this.expunged, that.expunged);
+            }
+            return false;
+        }
+
+        @Override
+        public final int hashCode() {
+            return Objects.hash(sessionId, user, path, mailboxId, expunged);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/9e5b1bf1/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
index a276c3f..1316753 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
@@ -48,4 +48,9 @@ class MailboxListenerTest {
     void addedShouldMatchBeanContract() {
         EqualsVerifier.forClass(MailboxListener.Added.class).verify();
     }
+
+    @Test
+    void expungedShouldMatchBeanContract() {
+        EqualsVerifier.forClass(MailboxListener.Expunged.class).verify();
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/9e5b1bf1/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
index 549e105..a638165 100644
--- a/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
+++ b/mailbox/event/json/src/main/scala/org/apache/james/event/json/EventSerializer.scala
@@ -28,9 +28,9 @@ import org.apache.james.core.quota.{QuotaCount, QuotaSize, QuotaValue}
 import org.apache.james.core.{Domain, User}
 import org.apache.james.event.json.DTOs.{ACLDiff, MailboxPath, Quota}
 import org.apache.james.event.json.MetaDataDTO.Flags
-import org.apache.james.mailbox.MailboxListener.{Added => JavaAdded, MailboxACLUpdated => JavaMailboxACLUpdated,
-  MailboxAdded => JavaMailboxAdded, MailboxDeletion => JavaMailboxDeletion, MailboxRenamed => JavaMailboxRenamed,
-  QuotaUsageUpdatedEvent => JavaQuotaUsageUpdatedEvent}
+import org.apache.james.mailbox.MailboxListener.{Added => JavaAdded, Expunged => JavaExpunged,
+  MailboxACLUpdated => JavaMailboxACLUpdated, MailboxAdded => JavaMailboxAdded, MailboxDeletion => JavaMailboxDeletion,
+  MailboxRenamed => JavaMailboxRenamed, QuotaUsageUpdatedEvent => JavaQuotaUsageUpdatedEvent}
 import org.apache.james.mailbox.MailboxSession.SessionId
 import org.apache.james.mailbox.model.{MailboxId, MessageId, QuotaRoot, MailboxACL => JavaMailboxACL, Quota => JavaQuota}
 import org.apache.james.mailbox.{MessageUid, Event => JavaEvent}
@@ -76,6 +76,16 @@ private object DTO {
       mailboxId,
       added.map(entry => entry._1 -> entry._2.toJava).asJava)
   }
+
+  case class Expunged(sessionId: SessionId, user: User, path: MailboxPath, mailboxId: MailboxId,
+                      expunged: Map[MessageUid, MetaDataDTO.MessageMetaData]) extends Event {
+    override def toJava: JavaEvent = new JavaExpunged(
+      sessionId,
+      user,
+      path.toJava,
+      mailboxId,
+      expunged.map(entry => entry._1 -> entry._2.toJava).asJava)
+  }
 }
 
 private object ScalaConverter {
@@ -124,6 +134,14 @@ private object ScalaConverter {
     added = event.getAdded.asScala.map(entry => entry._1 -> MetaDataDTO.MessageMetaData.fromJava(entry._2)).toMap
   )
 
+  private def toScala(event: JavaExpunged): DTO.Expunged = DTO.Expunged(
+    sessionId = event.getSessionId,
+    user = event.getUser,
+    path = MailboxPath.fromJava(event.getMailboxPath),
+    mailboxId = event.getMailboxId,
+    expunged = event.getExpunged.asScala.map(entry => entry._1 -> MetaDataDTO.MessageMetaData.fromJava(entry._2)).toMap
+  )
+
   def toScala(javaEvent: JavaEvent): Event = javaEvent match {
     case e: JavaMailboxACLUpdated => toScala(e)
     case e: JavaMailboxAdded => toScala(e)
@@ -131,6 +149,7 @@ private object ScalaConverter {
     case e: JavaMailboxRenamed => toScala(e)
     case e: JavaQuotaUsageUpdatedEvent => toScala(e)
     case e: JavaAdded => toScala(e)
+    case e: JavaExpunged => toScala(e)
     case _ => throw new RuntimeException("no Scala conversion known")
   }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/9e5b1bf1/mailbox/event/json/src/test/java/org/apache/james/event/json/ExpungedSerializationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/event/json/src/test/java/org/apache/james/event/json/ExpungedSerializationTest.java b/mailbox/event/json/src/test/java/org/apache/james/event/json/ExpungedSerializationTest.java
new file mode 100644
index 0000000..73521c7
--- /dev/null
+++ b/mailbox/event/json/src/test/java/org/apache/james/event/json/ExpungedSerializationTest.java
@@ -0,0 +1,1465 @@
+/****************************************************************
+ * 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.event.json;
+
+import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
+import static net.javacrumbs.jsonunit.core.Option.IGNORING_ARRAY_ORDER;
+import static org.apache.james.mailbox.model.MailboxConstants.USER_NAMESPACE;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.time.Instant;
+import java.util.Date;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import javax.mail.Flags;
+
+import org.apache.james.core.User;
+import org.apache.james.mailbox.FlagsBuilder;
+import org.apache.james.mailbox.MailboxListener;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageUid;
+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.MessageMetaData;
+import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.TestMessageId;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+class ExpungedSerializationTest {
+
+    private static final User USER = User.fromUsername("user");
+    private static final MailboxSession.SessionId SESSION_ID = MailboxSession.SessionId.of(42);
+    private static final MailboxId MAILBOX_ID = TestId.of(18);
+    private static final String MAILBOX_NAME = "mailboxName";
+    private static final MailboxPath MAILBOX_PATH = new MailboxPath(MailboxConstants.USER_NAMESPACE, "user", MAILBOX_NAME);
+    private static final MessageUid MESSAGE_UID = MessageUid.of(123456);
+    private static final Instant INSTANT = Instant.parse("2018-12-14T09:41:51.541Z");
+    private static final TestMessageId MESSAGE_ID = TestMessageId.of(42);
+    private static final int MOD_SEQ = 35;
+    private static final int SIZE = 45;
+    private static final Flags FLAGS = FlagsBuilder.builder()
+        .add(Flags.Flag.ANSWERED, Flags.Flag.DRAFT)
+        .add("User Custom Flag")
+        .build();
+    private static final Map<MessageUid, MessageMetaData> EXPUNGED = ImmutableMap.of(
+        MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(INSTANT), MESSAGE_ID));
+
+    private static final MailboxListener.Expunged DEFAULT_EXPUNGED_EVENT = new MailboxListener.Expunged(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, EXPUNGED);
+    private static final String DEFAULT_EXPUNGED_EVENT_JSON =
+        "{" +
+        "  \"Expunged\": {" +
+        "    \"path\": {" +
+        "      \"namespace\": \"#private\"," +
+        "      \"user\": \"user\"," +
+        "      \"name\": \"mailboxName\"" +
+        "    }," +
+        "    \"mailboxId\": \"18\"," +
+        "    \"expunged\": {" +
+        "      \"123456\": {" +
+        "        \"uid\": 123456," +
+        "        \"modSeq\": 35," +
+        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+        "        \"size\": 45,  " +
+        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+        "        \"messageId\": \"42\"" +
+        "      }" +
+        "    }," +
+        "    \"sessionId\": 42," +
+        "    \"user\": \"user\"" +
+        "  }" +
+        "}";
+
+    private static final EventSerializer EVENT_SERIALIZER = new EventSerializer(new TestId.Factory(), new TestMessageId.Factory());
+
+    @Test
+    void expungedShouldBeWellSerialized() {
+        assertThatJson(EVENT_SERIALIZER.toJson(DEFAULT_EXPUNGED_EVENT))
+            .isEqualTo(DEFAULT_EXPUNGED_EVENT_JSON);
+    }
+
+    @Test
+    void expungedShouldBeWellDeSerialized() {
+        assertThat(EVENT_SERIALIZER.fromJson(DEFAULT_EXPUNGED_EVENT_JSON).get())
+            .isEqualTo(DEFAULT_EXPUNGED_EVENT);
+    }
+
+    @Nested
+    class WithEmptyExpungedMap {
+
+        private final MailboxListener.Expunged emptyExpungedEvent = new MailboxListener.Expunged(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, ImmutableMap.of());
+        private final String emptyExpungedEventJson =
+            "{" +
+            "  \"Expunged\": {" +
+            "    \"path\": {" +
+            "      \"namespace\": \"#private\"," +
+            "      \"user\": \"user\"," +
+            "      \"name\": \"mailboxName\"" +
+            "    }," +
+            "    \"mailboxId\": \"18\"," +
+            "    \"expunged\": {}," +
+            "    \"sessionId\": 42," +
+            "    \"user\": \"user\"" +
+            "  }" +
+            "}";
+
+        @Test
+        void expungedShouldBeWellSerializedWhenMapKeyIsEmpty() {
+            assertThatJson(EVENT_SERIALIZER.toJson(emptyExpungedEvent))
+                .isEqualTo(emptyExpungedEventJson);
+        }
+
+        @Test
+        void expungedShouldBeWellDeSerializedWhenMapKeyIsEmpty() {
+            assertThat(EVENT_SERIALIZER.fromJson(emptyExpungedEventJson).get())
+                .isEqualTo(emptyExpungedEvent);
+        }
+    }
+
+    @Nested
+    class WithFlags {
+
+        @Nested
+        class WithEmptyFlags {
+            private final Flags emptyFlags = new FlagsBuilder().build();
+            private final MailboxListener.Expunged emptyFlagsExpungedEvent = new MailboxListener.Expunged(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID,
+                ImmutableMap.of(
+                    MESSAGE_UID,
+                    new MessageMetaData(MESSAGE_UID, MOD_SEQ, emptyFlags, SIZE, Date.from(INSTANT), MESSAGE_ID)));
+
+            private final String emptyFlagsExpungedEventJson =
+                "{" +
+                "  \"Expunged\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"expunged\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": []," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}";
+
+            @Test
+            void expungedShouldBeWellSerializedWhenEmptyFlags() {
+                assertThatJson(EVENT_SERIALIZER.toJson(emptyFlagsExpungedEvent))
+                    .isEqualTo(emptyFlagsExpungedEventJson);
+            }
+
+            @Test
+            void expungedShouldBeWellDeSerializedWhenEmptyFlags() {
+                assertThat(EVENT_SERIALIZER.fromJson(emptyFlagsExpungedEventJson).get())
+                    .isEqualTo(emptyFlagsExpungedEvent);
+            }
+        }
+
+        @Nested
+        class WithOnlyUserFlags {
+            private final Flags onlyUserFlags = new FlagsBuilder()
+                .add("Custom 1", "Custom 2", "")
+                .build();
+            private final MailboxListener.Expunged onlyUserFlagsExpungedEvent = new MailboxListener.Expunged(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID,
+                ImmutableMap.of(
+                    MESSAGE_UID,
+                    new MessageMetaData(MESSAGE_UID, MOD_SEQ, onlyUserFlags, SIZE, Date.from(INSTANT), MESSAGE_ID)));
+
+            private final String userOnlyFlagsExpungedEventJson =
+                "{" +
+                "  \"Expunged\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"expunged\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"Custom 1\", \"Custom 2\", \"\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}";
+
+            @Test
+            void expungedShouldBeWellSerializedWhenOnlyUserFlags() {
+                assertThatJson(EVENT_SERIALIZER.toJson(onlyUserFlagsExpungedEvent))
+                    .when(IGNORING_ARRAY_ORDER)
+                    .isEqualTo(userOnlyFlagsExpungedEventJson);
+            }
+
+            @Test
+            void expungedShouldBeWellDeSerializedWhenOnlyUserFlags() {
+                assertThat(EVENT_SERIALIZER.fromJson(userOnlyFlagsExpungedEventJson).get())
+                    .isEqualTo(onlyUserFlagsExpungedEvent);
+            }
+        }
+
+        @Nested
+        class WithOnlySystemFlags {
+            private final Flags onlySystemFlags = new FlagsBuilder()
+                .add(Flags.Flag.SEEN, Flags.Flag.ANSWERED, Flags.Flag.DELETED)
+                .build();
+            private final MailboxListener.Expunged onlySystemFlagsExpungedEvent = new MailboxListener.Expunged(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID,
+                ImmutableMap.of(
+                    MESSAGE_UID,
+                    new MessageMetaData(MESSAGE_UID, MOD_SEQ, onlySystemFlags, SIZE, Date.from(INSTANT), MESSAGE_ID)));
+
+            private final String systemOnlyFlagsExpungedEventJson =
+                "{" +
+                "  \"Expunged\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"expunged\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"\\\\Seen\", \"\\\\Answered\", \"\\\\Deleted\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}";
+
+            @Test
+            void expungedShouldBeWellSerializedWhenOnlySystemFlags() {
+                assertThatJson(EVENT_SERIALIZER.toJson(onlySystemFlagsExpungedEvent))
+                    .when(IGNORING_ARRAY_ORDER)
+                    .isEqualTo(systemOnlyFlagsExpungedEventJson);
+            }
+
+            @Test
+            void expungedShouldBeWellDeSerializedWhenOnlySystemFlags() {
+                assertThat(EVENT_SERIALIZER.fromJson(systemOnlyFlagsExpungedEventJson).get())
+                    .isEqualTo(onlySystemFlagsExpungedEvent);
+            }
+        }
+
+        @Nested
+        class WithFlagCaseSensitive {
+
+            private static final String CASE_SENSITIVE_SYSTEM_FLAGS_EVENT_JSON =
+                "{" +
+                "  \"Expunged\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"expunged\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"User Custom Flag\", \"\\\\answereD\", \"\\\\dRaFt\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}";
+
+             @Test
+            void expungedShouldCareAboutSystemFlagsCaseSensitive() {
+                 MailboxListener.Expunged deSerializedEvent = (MailboxListener.Expunged) EVENT_SERIALIZER
+                     .fromJson(CASE_SENSITIVE_SYSTEM_FLAGS_EVENT_JSON)
+                     .get();
+
+                 assertThat(deSerializedEvent.getMetaData(MESSAGE_UID).getFlags().getSystemFlags())
+                     .isEmpty();
+            }
+        }
+    }
+
+    @Nested
+    class WithInternalDate {
+
+        @Test
+        void expungedShouldDeserializeWhenInternalDateIsInGoodISOFormat() {
+            Map<MessageUid, MessageMetaData> Expunged = ImmutableMap.of(
+                MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(Instant.parse("2018-12-14T09:41:51Z")), MESSAGE_ID));
+            MailboxListener.Expunged eventRoundToMillis = new MailboxListener.Expunged(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, Expunged);
+
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Expunged\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"expunged\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51+00:00\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}").get())
+            .isEqualTo(eventRoundToMillis);
+        }
+
+        @Test
+        void expungedShouldDeserializeWhenInternalDateIsMissingMilliSeconds() {
+            Map<MessageUid, MessageMetaData> Expunged = ImmutableMap.of(
+                MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(Instant.parse("2018-12-14T09:41:51Z")), MESSAGE_ID));
+            MailboxListener.Expunged eventRoundToMillis = new MailboxListener.Expunged(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, Expunged);
+
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Expunged\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"expunged\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}").get())
+            .isEqualTo(eventRoundToMillis);
+        }
+
+        @Test
+        void expungedShouldDeserializeWhenInternalDateIsMissingSeconds() {
+            Map<MessageUid, MessageMetaData> Expunged = ImmutableMap.of(
+                MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(Instant.parse("2018-12-14T09:41:00Z")), MESSAGE_ID));
+            MailboxListener.Expunged eventRoundToMinute = new MailboxListener.Expunged(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, Expunged);
+
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Expunged\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"expunged\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}").get())
+            .isEqualTo(eventRoundToMinute);
+        }
+    }
+    @Nested
+    class NullOrEmptyNameSpaceInMailboxPath {
+
+        @Test
+        void expungedShouldBeWellDeSerializedWhenNullNameSpace() {
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Expunged\": {" +
+                "    \"path\": {" +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"expunged\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}").get())
+            .isEqualTo(DEFAULT_EXPUNGED_EVENT);
+        }
+
+        @Test
+        void expungedShouldBeWellDeSerializedWhenEmptyNameSpace() {
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Expunged\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"expunged\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}").get())
+            .isEqualTo(DEFAULT_EXPUNGED_EVENT);
+        }
+    }
+
+    @Nested
+    class NullUserInMailboxPath {
+        private final String nullUser = null;
+        private final MailboxListener.Expunged eventWithNullUserInPath = new MailboxListener.Expunged(
+            SESSION_ID,
+            USER,
+            new MailboxPath(USER_NAMESPACE, nullUser, MAILBOX_NAME),
+            MAILBOX_ID,
+            EXPUNGED);
+
+        private static final String EVENT_JSON_WITH_NULL_USER_IN_PATH =
+            "{" +
+            "  \"Expunged\": {" +
+            "    \"path\": {" +
+            "      \"namespace\": \"#private\"," +
+            "      \"name\": \"mailboxName\"" +
+            "    }," +
+            "    \"mailboxId\": \"18\"," +
+            "    \"expunged\": {" +
+            "      \"123456\": {" +
+            "        \"uid\": 123456," +
+            "        \"modSeq\": 35," +
+            "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+            "        \"size\": 45,  " +
+            "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+            "        \"messageId\": \"42\"" +
+            "      }" +
+            "    }," +
+            "    \"sessionId\": 42," +
+            "    \"user\": \"user\"" +
+            "  }" +
+            "}";
+
+        @Test
+        void expungedShouldBeWellSerialized() {
+            assertThatJson(EVENT_SERIALIZER.toJson(eventWithNullUserInPath))
+                .isEqualTo(EVENT_JSON_WITH_NULL_USER_IN_PATH);
+        }
+
+        @Test
+        void expungedShouldBeWellDeSerialized() {
+            assertThat(EVENT_SERIALIZER.fromJson(EVENT_JSON_WITH_NULL_USER_IN_PATH).get())
+                .isEqualTo(eventWithNullUserInPath);
+        }
+    }
+
+    @Nested
+    class DeserializationErrors {
+
+        @Nested
+        class DeserializationErrorOnSessionId {
+            @Test
+            void expungedShouldThrowWhenMissingSessionId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"expunged\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void expungedShouldThrowWhenNullSessionId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"expunged\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": null," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void expungedShouldThrowWhenStringSessionId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"expunged\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": \"42\"," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+        }
+
+        @Nested
+        class DeserializationErrorOnUser {
+            @Test
+            void expungedShouldThrowWhenMissingUser() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"expunged\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": 42" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void expungedShouldThrowWhenUserIsNotAString() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"expunged\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": 596" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void expungedShouldThrowWhenUserIsNotWellFormatted() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"expunged\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": \"user@user@anotherUser\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(IllegalArgumentException.class);
+            }
+        }
+
+        @Nested
+        class DeserializationErrorOnMailboxId {
+            @Test
+            void expungedShouldThrowWhenMissingMailboxId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"expunged\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void expungedShouldThrowWhenNullMailboxId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": null," +
+                    "    \"expunged\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Test
+            void expungedShouldThrowWhenMailboxIdIsANumber() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": 18," +
+                    "    \"expunged\": {" +
+                    "      \"123456\": {" +
+                    "        \"uid\": 123456," +
+                    "        \"modSeq\": 35," +
+                    "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                    "        \"size\": 45,  " +
+                    "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                    "        \"messageId\": \"42\"" +
+                    "      }" +
+                    "    }," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+        }
+
+        @Nested
+        class DeserializationErrorOnMailboxPath {
+
+            @Nested
+            class DeserializationErrorOnNameSpace {
+                @Test
+                void expungedShouldThrowWhenNameSpaceIsNotAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 48246," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnUser {
+                @Test
+                void expungedShouldThrowWhenUserIsNotAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": 265412.64," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnMailboxName {
+
+                @Test
+                void expungedShouldThrowWhenNullMailboxName() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": null" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenMailboxNameIdIsANumber() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": 11861" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+        }
+
+        @Nested
+        class DeserializationErrorOnExpungedMap {
+            @Test
+            void expungedShouldThrowWhenMapKeyIsNull() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Expunged\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"Expunged\": null," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Nested
+            class DeserializationErrorOnMessageUid {
+
+                @Test
+                void expungedShouldThrowWhenMessageUidIsAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": null" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": \"123456\"," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenMessageUidIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": null" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": null," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnModSeq {
+
+                @Test
+                void expungedShouldThrowWhenModSeqIsAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": \"35\"," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenModSeqIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": null" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": null," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+
+            @Nested
+            class DeserializationErrorOnSize {
+
+                @Test
+                void expungedShouldThrowWhenSizeIsAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": \"45\",  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenSizeIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": null,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnMessageId {
+
+                @Test
+                void expungedShouldThrowWhenMessageIdIsANumber() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": 42" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenMessageIdIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                        "        \"messageId\": null" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnInternalDate {
+                @Test
+                void expungedShouldThrowWhenInternalDateIsNotInISOFormatBecauseOfMissingTWord() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14 12:52:36+07:00\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenInternalDateContainsOnlyDate() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenInternalDateIsMissingHourPart() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14TZ\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenInternalDateIsMissingTimeZone() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14T09:41:51.541\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenInternalDateIsMissingHours() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"2018-12-14Z\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenInternalDateIsEmpty() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenInternalDateIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"User Custom Flag\", \"\\\\Answered\", \"\\\\Draft\"]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": null," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+
+            @Nested
+            class DeserializationErrorOnFlags {
+
+                @Test
+                void expungedShouldThrowWhenFlagsIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Expunged\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"expunged\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": null," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenFlagsContainsNullElements() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"flag 1\", null, \"flags 2\", null]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void expungedShouldThrowWhenFlagsContainsNumberElements() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": [\"flag 1\", 1254, \"flags 2\", 125.36]," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+            }
+        }
+    }
+}


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