MAILBOX-359 Added Scala Event Serialization

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

Branch: refs/heads/master
Commit: 39bc507acae024f4686a78c750f09a494cde1282
Parents: 0f9024f
Author: tran tien duc <[email protected]>
Authored: Fri Dec 14 17:49:09 2018 +0700
Committer: Benoit Tellier <[email protected]>
Committed: Wed Dec 19 18:05:57 2018 +0700

----------------------------------------------------------------------
 .../org/apache/james/mailbox/FlagsBuilder.java  |    5 +
 .../apache/james/mailbox/MailboxListener.java   |   26 +-
 .../james/mailbox/MailboxListenerTest.java      |    5 +
 .../james/event/json/EventSerializer.scala      |   73 +-
 .../apache/james/event/json/MetaDataDTO.scala   |   90 ++
 .../event/json/AddedSerializationTest.java      | 1465 ++++++++++++++++++
 ...MailboxACLUpdatedEventSerializationTest.java |    3 +-
 .../json/MailboxAddedSerializationTest.java     |    3 +-
 .../json/MailboxDeletionSerializationTest.java  |    3 +-
 .../json/MailboxRenamedSerializationTest.java   |    3 +-
 ...QuotaUsageUpdatedEventSerializationTest.java |    3 +-
 11 files changed, 1663 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java
----------------------------------------------------------------------
diff --git 
a/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java 
b/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java
index 7a791ec..f36189b 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java
@@ -61,6 +61,11 @@ public class FlagsBuilder {
         return this;
     }
 
+    public FlagsBuilder merge(FlagsBuilder flagsBuilder) {
+        internalFlags.add(flagsBuilder.internalFlags);
+        return this;
+    }
+
     public Flags build() {
         return new Flags(internalFlags);
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/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 b3d4785..2b7fdb9 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
@@ -24,7 +24,6 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.SortedMap;
 
 import org.apache.james.core.User;
 import org.apache.james.core.quota.QuotaCount;
@@ -429,7 +428,7 @@ public interface MailboxListener {
     class Added extends MetaDataHoldingEvent {
         private final Map<MessageUid, MessageMetaData> added;
 
-        public Added(MailboxSession.SessionId sessionId, User user, 
MailboxPath path, MailboxId mailboxId, SortedMap<MessageUid, MessageMetaData> 
uids) {
+        public Added(MailboxSession.SessionId sessionId, User user, 
MailboxPath path, MailboxId mailboxId, Map<MessageUid, MessageMetaData> uids) {
             super(sessionId, user, path, mailboxId);
             this.added = ImmutableMap.copyOf(uids);
         }
@@ -447,6 +446,29 @@ public interface MailboxListener {
         public Collection<MessageUid> getUids() {
             return added.keySet();
         }
+
+        public Map<MessageUid, MessageMetaData> getAdded() {
+            return added;
+        }
+
+        @Override
+        public final boolean equals(Object o) {
+            if (o instanceof Added) {
+                Added that = (Added) 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.added, that.added);
+            }
+            return false;
+        }
+
+        @Override
+        public final int hashCode() {
+            return Objects.hash(sessionId, user, path, mailboxId, added);
+        }
     }
 
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/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 ac9c364..a276c3f 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
@@ -43,4 +43,9 @@ class MailboxListenerTest {
     void mailboxACLUpdatedShouldMatchBeanContract() {
         
EqualsVerifier.forClass(MailboxListener.MailboxACLUpdated.class).verify();
     }
+
+    @Test
+    void addedShouldMatchBeanContract() {
+        EqualsVerifier.forClass(MailboxListener.Added.class).verify();
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/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 5a14668..549e105 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
@@ -22,15 +22,21 @@ package org.apache.james.event.json
 import java.time.Instant
 import java.util.Optional
 
+import javax.mail.{Flags => JavaMailFlags}
 import julienrf.json.derived
 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.mailbox.MailboxListener.{MailboxACLUpdated => 
JavaMailboxACLUpdated, MailboxAdded => JavaMailboxAdded, MailboxDeletion => 
JavaMailboxDeletion, MailboxRenamed => JavaMailboxRenamed, 
QuotaUsageUpdatedEvent => JavaQuotaUsageUpdatedEvent}
+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.MailboxSession.SessionId
-import org.apache.james.mailbox.model.{MailboxId, QuotaRoot, MailboxACL => 
JavaMailboxACL, Quota => JavaQuota}
-import org.apache.james.mailbox.{Event => JavaEvent}
-import play.api.libs.json.{JsError, JsNull, JsNumber, JsObject, JsResult, 
JsString, JsSuccess, Json, OFormat, Reads, Writes}
+import org.apache.james.mailbox.model.{MailboxId, MessageId, QuotaRoot, 
MailboxACL => JavaMailboxACL, Quota => JavaQuota}
+import org.apache.james.mailbox.{MessageUid, Event => JavaEvent}
+import play.api.libs.json.{JsArray, JsError, JsNull, JsNumber, JsObject, 
JsResult, JsString, JsSuccess, Json, OFormat, Reads, Writes}
+
+import scala.collection.JavaConverters._
 
 private sealed trait Event {
   def toJava: JavaEvent
@@ -60,6 +66,16 @@ private object DTO {
                                     sizeQuota: Quota[QuotaSize], time: 
Instant) extends Event {
     override def toJava: JavaEvent = new JavaQuotaUsageUpdatedEvent(user, 
quotaRoot, countQuota.toJava, sizeQuota.toJava, time)
   }
+
+  case class Added(sessionId: SessionId, user: User, path: MailboxPath, 
mailboxId: MailboxId,
+                   added: Map[MessageUid, MetaDataDTO.MessageMetaData]) 
extends Event {
+    override def toJava: JavaEvent = new JavaAdded(
+      sessionId,
+      user,
+      path.toJava,
+      mailboxId,
+      added.map(entry => entry._1 -> entry._2.toJava).asJava)
+  }
 }
 
 private object ScalaConverter {
@@ -85,6 +101,7 @@ private object ScalaConverter {
     totalDeletedSize = event.getTotalDeletedSize,
     mailboxId = event.getMailboxId)
 
+
   private def toScala(event: JavaMailboxRenamed): DTO.MailboxRenamed = 
DTO.MailboxRenamed(
     sessionId = event.getSessionId,
     user = event.getUser,
@@ -99,17 +116,26 @@ private object ScalaConverter {
     sizeQuota = Quota.toScala(event.getSizeQuota),
     time = event.getInstant)
 
+  private def toScala(event: JavaAdded): DTO.Added = DTO.Added(
+    sessionId = event.getSessionId,
+    user = event.getUser,
+    path = MailboxPath.fromJava(event.getMailboxPath),
+    mailboxId = event.getMailboxId,
+    added = event.getAdded.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)
     case e: JavaMailboxDeletion => toScala(e)
     case e: JavaMailboxRenamed => toScala(e)
     case e: JavaQuotaUsageUpdatedEvent => toScala(e)
-    case _ => throw new RuntimeException("no Scala convertion known")
+    case e: JavaAdded => toScala(e)
+    case _ => throw new RuntimeException("no Scala conversion known")
   }
 }
 
-private class JsonSerialize(mailboxIdFactory: MailboxId.Factory) {
+private class JsonSerialize(mailboxIdFactory: MailboxId.Factory, 
messageIdFactory: MessageId.Factory) {
   implicit val userWriters: Writes[User] = (user: User) => 
JsString(user.asString)
   implicit val quotaRootWrites: Writes[QuotaRoot] = quotaRoot => 
JsString(quotaRoot.getValue)
   implicit val quotaValueWrites: Writes[QuotaValue[_]] = value => if 
(value.isUnlimited) JsNull else JsNumber(value.asLong())
@@ -122,6 +148,10 @@ private class JsonSerialize(mailboxIdFactory: 
MailboxId.Factory) {
   implicit val aclEntryKeyWrites: Writes[JavaMailboxACL.EntryKey] = value => 
JsString(value.serialize())
   implicit val aclRightsWrites: Writes[JavaMailboxACL.Rfc4314Rights] = value 
=> JsString(value.serialize())
   implicit val aclDiffWrites: Writes[ACLDiff] = Json.writes[ACLDiff]
+  implicit val messageIdWrites: Writes[MessageId] = value => 
JsString(value.serialize())
+  implicit val messageUidWrites: Writes[MessageUid] = value => 
JsNumber(value.asLong())
+  implicit val flagsWrites: Writes[JavaMailFlags] = value => 
JsArray(Flags.fromJavaFlags(value).map(flag => JsString(flag)))
+  implicit val messageMetaDataWrites: Writes[MetaDataDTO.MessageMetaData] = 
Json.writes[MetaDataDTO.MessageMetaData]
 
   implicit val aclEntryKeyReads: Reads[JavaMailboxACL.EntryKey] = {
     case JsString(keyAsString) => 
JsSuccess(JavaMailboxACL.EntryKey.deserialize(keyAsString))
@@ -161,6 +191,18 @@ private class JsonSerialize(mailboxIdFactory: 
MailboxId.Factory) {
     case JsString(userAsString) => JsSuccess(User.fromUsername(userAsString))
     case _ => JsError()
   }
+  implicit val messageIdReads: Reads[MessageId] = {
+    case JsString(value) => JsSuccess(messageIdFactory.fromString(value))
+    case _ => JsError()
+  }
+  implicit val messageUidReads: Reads[MessageUid] = {
+    case JsNumber(value) => JsSuccess(MessageUid.of(value.toLong))
+    case _ => JsError()
+  }
+  implicit val flagsReads: Reads[JavaMailFlags] = {
+    case JsArray(seqOfJsValues) => 
JsSuccess(Flags.toJavaFlags(seqOfJsValues.toArray.map(jsValue => 
jsValue.toString())))
+    case _ => JsError()
+  }
 
   implicit def scopeMapReads[V](implicit vr: Reads[V]): 
Reads[Map[JavaQuota.Scope, V]] =
     Reads.mapReads[JavaQuota.Scope, V] { str =>
@@ -172,6 +214,7 @@ private class JsonSerialize(mailboxIdFactory: 
MailboxId.Factory) {
       JsObject(m.map { case (k, v) => (k.toString, vr.writes(v)) }.toSeq)
     }
 
+
   implicit def scopeMapReadsACL[V](implicit vr: Reads[V]): 
Reads[Map[JavaMailboxACL.EntryKey, V]] =
     Reads.mapReads[JavaMailboxACL.EntryKey, V] { str =>
       Json.fromJson[JavaMailboxACL.EntryKey](JsString(str))
@@ -182,10 +225,21 @@ private class JsonSerialize(mailboxIdFactory: 
MailboxId.Factory) {
       JsObject(m.map { case (k, v) => (k.toString, vr.writes(v)) }.toSeq)
     }
 
+  implicit def scopeMessageUidMapReads[V](implicit vr: Reads[V]): 
Reads[Map[MessageUid, V]] =
+    Reads.mapReads[MessageUid, V] { str =>
+      JsSuccess(MessageUid.of(str.toLong))
+    }
+
+  implicit def scopeMessageUidMapWrite[V](implicit vr: Writes[V]): 
Writes[Map[MessageUid, V]] =
+    (m: Map[MessageUid, V]) => {
+      JsObject(m.map { case (k, v) => (String.valueOf(k.asLong()), 
vr.writes(v)) }.toSeq)
+    }
+
   implicit val aclDiffReads: Reads[ACLDiff] = Json.reads[ACLDiff]
   implicit val mailboxPathReads: Reads[MailboxPath] = Json.reads[MailboxPath]
   implicit val quotaCReads: Reads[Quota[QuotaCount]] = 
Json.reads[Quota[QuotaCount]]
   implicit val quotaSReads: Reads[Quota[QuotaSize]] = 
Json.reads[Quota[QuotaSize]]
+  implicit val messageMetaDataReads: Reads[MetaDataDTO.MessageMetaData] = 
Json.reads[MetaDataDTO.MessageMetaData]
 
   implicit val eventOFormat: OFormat[Event] = derived.oformat()
 
@@ -194,12 +248,13 @@ private class JsonSerialize(mailboxIdFactory: 
MailboxId.Factory) {
   def fromJson(json: String): JsResult[Event] = 
Json.fromJson[Event](Json.parse(json))
 }
 
-class EventSerializer(mailboxIdFactory: MailboxId.Factory) {
-  def toJson(event: JavaEvent): String = new 
JsonSerialize(mailboxIdFactory).toJson(ScalaConverter.toScala(event))
+class EventSerializer(mailboxIdFactory: MailboxId.Factory, messageIdFactory: 
MessageId.Factory) {
+  def toJson(event: JavaEvent): String = new JsonSerialize(mailboxIdFactory, 
messageIdFactory).toJson(ScalaConverter.toScala(event))
 
   def fromJson(json: String): JsResult[JavaEvent] = {
-    new JsonSerialize(mailboxIdFactory)
+    new JsonSerialize(mailboxIdFactory, messageIdFactory)
       .fromJson(json)
       .map(event => event.toJava)
   }
 }
+

http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/mailbox/event/json/src/main/scala/org/apache/james/event/json/MetaDataDTO.scala
----------------------------------------------------------------------
diff --git 
a/mailbox/event/json/src/main/scala/org/apache/james/event/json/MetaDataDTO.scala
 
b/mailbox/event/json/src/main/scala/org/apache/james/event/json/MetaDataDTO.scala
new file mode 100644
index 0000000..ee314f0
--- /dev/null
+++ 
b/mailbox/event/json/src/main/scala/org/apache/james/event/json/MetaDataDTO.scala
@@ -0,0 +1,90 @@
+/** **************************************************************
+  * 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 java.time.Instant
+import java.util.Date
+
+import javax.mail.{Flags => JavaMailFlags}
+import org.apache.james.mailbox.model.{MessageId, MessageMetaData => 
JavaMessageMetaData}
+import org.apache.james.mailbox.{FlagsBuilder, MessageUid}
+
+object MetaDataDTO {
+
+  object MessageMetaData {
+    def fromJava(javaMessageMetaData: JavaMessageMetaData): MessageMetaData = 
MetaDataDTO.MessageMetaData(
+      javaMessageMetaData.getUid,
+      javaMessageMetaData.getModSeq,
+      javaMessageMetaData.getFlags,
+      javaMessageMetaData.getSize,
+      javaMessageMetaData.getInternalDate.toInstant,
+      javaMessageMetaData.getMessageId)
+  }
+
+  object Flags {
+    val ANSWERED = "\\Answered"
+    val DELETED = "\\Deleted"
+    val DRAFT = "\\Draft"
+    val FLAGGED = "\\Flagged"
+    val RECENT = "\\Recent"
+    val SEEN = "\\Seen"
+    val ALL_SYSTEM_FLAGS = List(ANSWERED, DELETED, DRAFT, FLAGGED, RECENT, 
SEEN)
+
+    def toJavaFlags(serializedFlags: Array[String]): JavaMailFlags = {
+      serializedFlags
+        .map(toFlagBuilder)
+        .reduceOption(_ merge _)
+        .getOrElse(new FlagsBuilder())
+        .build()
+    }
+
+    def toFlagBuilder(flag: String): FlagsBuilder = 
ALL_SYSTEM_FLAGS.contains(flag) match {
+      case true => new FlagsBuilder().add(stringToSystemFlag(flag))
+      case false => new FlagsBuilder().add(flag)
+    }
+
+    def fromJavaFlags(flags: JavaMailFlags): Array[String] = {
+      flags.getUserFlags ++ flags.getSystemFlags.map(flag => 
systemFlagToString(flag))
+    }
+
+    private def stringToSystemFlag(serializedFlag: String): JavaMailFlags.Flag 
= serializedFlag match {
+      case ANSWERED => JavaMailFlags.Flag.ANSWERED
+      case DELETED => JavaMailFlags.Flag.DELETED
+      case DRAFT => JavaMailFlags.Flag.DRAFT
+      case FLAGGED => JavaMailFlags.Flag.FLAGGED
+      case RECENT => JavaMailFlags.Flag.RECENT
+      case SEEN => JavaMailFlags.Flag.SEEN
+      case _ => throw new IllegalArgumentException(serializedFlag + " is not a 
system flag")
+    }
+
+    private def systemFlagToString(flag: JavaMailFlags.Flag): String = flag 
match {
+      case JavaMailFlags.Flag.ANSWERED => ANSWERED
+      case JavaMailFlags.Flag.DELETED => DELETED
+      case JavaMailFlags.Flag.DRAFT => DRAFT
+      case JavaMailFlags.Flag.FLAGGED => FLAGGED
+      case JavaMailFlags.Flag.RECENT => RECENT
+      case JavaMailFlags.Flag.SEEN => SEEN
+    }
+  }
+
+  case class MessageMetaData(uid: MessageUid, modSeq: Long, flags: 
JavaMailFlags, size: Long, internalDate: Instant, messageId: MessageId) {
+    def toJava: JavaMessageMetaData = new JavaMessageMetaData(uid, modSeq, 
flags, size, Date.from(internalDate), messageId)
+  }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java
----------------------------------------------------------------------
diff --git 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java
 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java
new file mode 100644
index 0000000..3ddc475
--- /dev/null
+++ 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.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 AddedSerializationTest {
+
+    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> ADDED = 
ImmutableMap.of(
+        MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, 
Date.from(INSTANT), MESSAGE_ID));
+
+    private static final MailboxListener.Added DEFAULT_ADDED_EVENT = new 
MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, ADDED);
+    private static final String DEFAULT_ADDED_EVENT_JSON = 
+        "{" +
+        "  \"Added\": {" +
+        "    \"path\": {" +
+        "      \"namespace\": \"#private\"," +
+        "      \"user\": \"user\"," +
+        "      \"name\": \"mailboxName\"" +
+        "    }," +
+        "    \"mailboxId\": \"18\"," +
+        "    \"added\": {" +
+        "      \"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 addedShouldBeWellSerialized() {
+        assertThatJson(EVENT_SERIALIZER.toJson(DEFAULT_ADDED_EVENT))
+            .isEqualTo(DEFAULT_ADDED_EVENT_JSON);
+    }
+
+    @Test
+    void addedShouldBeWellDeSerialized() {
+        assertThat(EVENT_SERIALIZER.fromJson(DEFAULT_ADDED_EVENT_JSON).get())
+            .isEqualTo(DEFAULT_ADDED_EVENT);
+    }
+
+    @Nested
+    class WithEmptyAddedMap {
+
+        private final MailboxListener.Added emptyAddedEvent = new 
MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, 
ImmutableMap.of());
+        private final String emptyAddedEventJson =
+            "{" +
+            "  \"Added\": {" +
+            "    \"path\": {" +
+            "      \"namespace\": \"#private\"," +
+            "      \"user\": \"user\"," +
+            "      \"name\": \"mailboxName\"" +
+            "    }," +
+            "    \"mailboxId\": \"18\"," +
+            "    \"added\": {}," +
+            "    \"sessionId\": 42," +
+            "    \"user\": \"user\"" +
+            "  }" +
+            "}";
+
+        @Test
+        void addedShouldBeWellSerializedWhenMapKeyIsEmpty() {
+            assertThatJson(EVENT_SERIALIZER.toJson(emptyAddedEvent))
+                .isEqualTo(emptyAddedEventJson);
+        }
+
+        @Test
+        void addedShouldBeWellDeSerializedWhenMapKeyIsEmpty() {
+            assertThat(EVENT_SERIALIZER.fromJson(emptyAddedEventJson).get())
+                .isEqualTo(emptyAddedEvent);
+        }
+    }
+
+    @Nested
+    class WithFlags {
+
+        @Nested
+        class WithEmptyFlags {
+            private final Flags emptyFlags = new FlagsBuilder().build();
+            private final MailboxListener.Added emptyFlagsAddedEvent = new 
MailboxListener.Added(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 emptyFlagsAddedEventJson =
+                "{" +
+                "  \"Added\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"added\": {" +
+                "      \"123456\": {" +
+                "        \"uid\": 123456," +
+                "        \"modSeq\": 35," +
+                "        \"flags\": []," +
+                "        \"size\": 45,  " +
+                "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+                "        \"messageId\": \"42\"" +
+                "      }" +
+                "    }," +
+                "    \"sessionId\": 42," +
+                "    \"user\": \"user\"" +
+                "  }" +
+                "}";
+
+            @Test
+            void addedShouldBeWellSerializedWhenEmptyFlags() {
+                assertThatJson(EVENT_SERIALIZER.toJson(emptyFlagsAddedEvent))
+                    .isEqualTo(emptyFlagsAddedEventJson);
+            }
+
+            @Test
+            void addedShouldBeWellDeSerializedWhenEmptyFlags() {
+                
assertThat(EVENT_SERIALIZER.fromJson(emptyFlagsAddedEventJson).get())
+                    .isEqualTo(emptyFlagsAddedEvent);
+            }
+        }
+
+        @Nested
+        class WithOnlyUserFlags {
+            private final Flags onlyUserFlags = new FlagsBuilder()
+                .add("Custom 1", "Custom 2", "")
+                .build();
+            private final MailboxListener.Added onlyUserFlagsAddedEvent = new 
MailboxListener.Added(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 userOnlyFlagsAddedEventJson =
+                "{" +
+                "  \"Added\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"added\": {" +
+                "      \"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 addedShouldBeWellSerializedWhenOnlyUserFlags() {
+                
assertThatJson(EVENT_SERIALIZER.toJson(onlyUserFlagsAddedEvent))
+                    .when(IGNORING_ARRAY_ORDER)
+                    .isEqualTo(userOnlyFlagsAddedEventJson);
+            }
+
+            @Test
+            void addedShouldBeWellDeSerializedWhenOnlyUserFlags() {
+                
assertThat(EVENT_SERIALIZER.fromJson(userOnlyFlagsAddedEventJson).get())
+                    .isEqualTo(onlyUserFlagsAddedEvent);
+            }
+        }
+
+        @Nested
+        class WithOnlySystemFlags {
+            private final Flags onlySystemFlags = new FlagsBuilder()
+                .add(Flags.Flag.SEEN, Flags.Flag.ANSWERED, Flags.Flag.DELETED)
+                .build();
+            private final MailboxListener.Added onlySystemFlagsAddedEvent = 
new MailboxListener.Added(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 systemOnlyFlagsAddedEventJson =
+                "{" +
+                "  \"Added\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"added\": {" +
+                "      \"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 addedShouldBeWellSerializedWhenOnlySystemFlags() {
+                
assertThatJson(EVENT_SERIALIZER.toJson(onlySystemFlagsAddedEvent))
+                    .when(IGNORING_ARRAY_ORDER)
+                    .isEqualTo(systemOnlyFlagsAddedEventJson);
+            }
+
+            @Test
+            void addedShouldBeWellDeSerializedWhenOnlySystemFlags() {
+                
assertThat(EVENT_SERIALIZER.fromJson(systemOnlyFlagsAddedEventJson).get())
+                    .isEqualTo(onlySystemFlagsAddedEvent);
+            }
+        }
+
+        @Nested
+        class WithFlagCaseSensitive {
+
+            private static final String CASE_SENSITIVE_SYSTEM_FLAGS_EVENT_JSON 
=
+                "{" +
+                "  \"Added\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"added\": {" +
+                "      \"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 addedShouldCareAboutSystemFlagsCaseSensitive() {
+                 MailboxListener.Added deSerializedEvent = 
(MailboxListener.Added) EVENT_SERIALIZER
+                     .fromJson(CASE_SENSITIVE_SYSTEM_FLAGS_EVENT_JSON)
+                     .get();
+
+                 
assertThat(deSerializedEvent.getMetaData(MESSAGE_UID).getFlags().getSystemFlags())
+                     .isEmpty();
+            }
+        }
+    }
+
+    @Nested
+    class WithInternalDate {
+
+        @Test
+        void addedShouldDeserializeWhenInternalDateIsInGoodISOFormat() {
+            Map<MessageUid, MessageMetaData> added = ImmutableMap.of(
+                MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, 
SIZE, Date.from(Instant.parse("2018-12-14T09:41:51Z")), MESSAGE_ID));
+            MailboxListener.Added eventRoundToMillis = new 
MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, added);
+
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Added\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"added\": {" +
+                "      \"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 addedShouldDeserializeWhenInternalDateIsMissingMilliSeconds() {
+            Map<MessageUid, MessageMetaData> added = ImmutableMap.of(
+                MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, 
SIZE, Date.from(Instant.parse("2018-12-14T09:41:51Z")), MESSAGE_ID));
+            MailboxListener.Added eventRoundToMillis = new 
MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, added);
+
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Added\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"added\": {" +
+                "      \"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 addedShouldDeserializeWhenInternalDateIsMissingSeconds() {
+            Map<MessageUid, MessageMetaData> added = ImmutableMap.of(
+                MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, 
SIZE, Date.from(Instant.parse("2018-12-14T09:41:00Z")), MESSAGE_ID));
+            MailboxListener.Added eventRoundToMinute = new 
MailboxListener.Added(SESSION_ID, USER, MAILBOX_PATH, MAILBOX_ID, added);
+
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Added\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"#private\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"added\": {" +
+                "      \"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 addedShouldBeWellDeSerializedWhenNullNameSpace() {
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Added\": {" +
+                "    \"path\": {" +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"added\": {" +
+                "      \"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_ADDED_EVENT);
+        }
+
+        @Test
+        void addedShouldBeWellDeSerializedWhenEmptyNameSpace() {
+            assertThat(EVENT_SERIALIZER.fromJson(
+                "{" +
+                "  \"Added\": {" +
+                "    \"path\": {" +
+                "      \"namespace\": \"\"," +
+                "      \"user\": \"user\"," +
+                "      \"name\": \"mailboxName\"" +
+                "    }," +
+                "    \"mailboxId\": \"18\"," +
+                "    \"added\": {" +
+                "      \"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_ADDED_EVENT);
+        }
+    }
+
+    @Nested
+    class NullUserInMailboxPath {
+        private final String nullUser = null;
+        private final MailboxListener.Added eventWithNullUserInPath = new 
MailboxListener.Added(
+            SESSION_ID,
+            USER,
+            new MailboxPath(USER_NAMESPACE, nullUser, MAILBOX_NAME),
+            MAILBOX_ID,
+            ADDED);
+
+        private static final String EVENT_JSON_WITH_NULL_USER_IN_PATH =
+            "{" +
+            "  \"Added\": {" +
+            "    \"path\": {" +
+            "      \"namespace\": \"#private\"," +
+            "      \"name\": \"mailboxName\"" +
+            "    }," +
+            "    \"mailboxId\": \"18\"," +
+            "    \"added\": {" +
+            "      \"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 addedShouldBeWellSerialized() {
+            assertThatJson(EVENT_SERIALIZER.toJson(eventWithNullUserInPath))
+                .isEqualTo(EVENT_JSON_WITH_NULL_USER_IN_PATH);
+        }
+
+        @Test
+        void addedShouldBeWellDeSerialized() {
+            
assertThat(EVENT_SERIALIZER.fromJson(EVENT_JSON_WITH_NULL_USER_IN_PATH).get())
+                .isEqualTo(eventWithNullUserInPath);
+        }
+    }
+
+    @Nested
+    class DeserializationErrors {
+
+        @Nested
+        class DeserializationErrorOnSessionId {
+            @Test
+            void addedShouldThrowWhenMissingSessionId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"added\": {" +
+                    "      \"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 addedShouldThrowWhenNullSessionId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"added\": {" +
+                    "      \"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 addedShouldThrowWhenStringSessionId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"added\": {" +
+                    "      \"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 addedShouldThrowWhenMissingUser() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"added\": {" +
+                    "      \"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 addedShouldThrowWhenUserIsNotAString() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"added\": {" +
+                    "      \"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 addedShouldThrowWhenUserIsNotWellFormatted() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"added\": {" +
+                    "      \"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 addedShouldThrowWhenMissingMailboxId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"added\": {" +
+                    "      \"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 addedShouldThrowWhenNullMailboxId() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": null," +
+                    "    \"added\": {" +
+                    "      \"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 addedShouldThrowWhenMailboxIdIsANumber() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": 18," +
+                    "    \"added\": {" +
+                    "      \"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 addedShouldThrowWhenNameSpaceIsNotAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": 48246," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 addedShouldThrowWhenUserIsNotAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": 265412.64," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 addedShouldThrowWhenNullMailboxName() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": null" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 addedShouldThrowWhenMailboxNameIdIsANumber() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": 11861" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 DeserializationErrorOnAddedMap {
+            @Test
+            void addedShouldThrowWhenMapKeyIsNull() {
+                assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                    "{" +
+                    "  \"Added\": {" +
+                    "    \"path\": {" +
+                    "      \"namespace\": \"#private\"," +
+                    "      \"user\": \"user\"," +
+                    "      \"name\": \"mailboxName\"" +
+                    "    }," +
+                    "    \"mailboxId\": \"18\"," +
+                    "    \"added\": null," +
+                    "    \"sessionId\": 42," +
+                    "    \"user\": \"user\"" +
+                    "  }" +
+                    "}").get())
+                .isInstanceOf(NoSuchElementException.class);
+            }
+
+            @Nested
+            class DeserializationErrorOnMessageUid {
+
+                @Test
+                void addedShouldThrowWhenMessageUidIsAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": null" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 addedShouldThrowWhenMessageUidIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": null" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 addedShouldThrowWhenModSeqIsAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 addedShouldThrowWhenModSeqIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": null" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 addedShouldThrowWhenSizeIsAString() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 addedShouldThrowWhenSizeIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 addedShouldThrowWhenMessageIdIsANumber() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 addedShouldThrowWhenMessageIdIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 
addedShouldThrowWhenInternalDateIsNotInISOFormatBecauseOfMissingTWord() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 addedShouldThrowWhenInternalDateContainsOnlyDate() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 addedShouldThrowWhenInternalDateIsMissingHourPart() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 addedShouldThrowWhenInternalDateIsMissingTimeZone() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 addedShouldThrowWhenInternalDateIsMissingHours() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 addedShouldThrowWhenInternalDateIsEmpty() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 addedShouldThrowWhenInternalDateIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"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 addedShouldThrowWhenFlagsIsNull() {
+                    assertThatThrownBy(() -> EVENT_SERIALIZER.fromJson(
+                        "{" +
+                        "  \"Added\": {" +
+                        "    \"path\": {" +
+                        "      \"namespace\": \"#private\"," +
+                        "      \"user\": \"user\"," +
+                        "      \"name\": \"mailboxName\"" +
+                        "    }," +
+                        "    \"mailboxId\": \"18\"," +
+                        "    \"added\": {" +
+                        "      \"123456\": {" +
+                        "        \"uid\": 123456," +
+                        "        \"modSeq\": 35," +
+                        "        \"flags\": null," +
+                        "        \"size\": 45,  " +
+                        "        \"internalDate\": \"\"," +
+                        "        \"messageId\": \"42\"" +
+                        "      }" +
+                        "    }," +
+                        "    \"sessionId\": 42," +
+                        "    \"user\": \"user\"" +
+                        "  }" +
+                        "}").get())
+                    .isInstanceOf(NoSuchElementException.class);
+                }
+
+                @Test
+                void addedShouldThrowWhenFlagsContainsNullElements() {
+                    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 addedShouldThrowWhenFlagsContainsNumberElements() {
+                    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);
+                }
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxACLUpdatedEventSerializationTest.java
----------------------------------------------------------------------
diff --git 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxACLUpdatedEventSerializationTest.java
 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxACLUpdatedEventSerializationTest.java
index ceed360..14d3bf1 100644
--- 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxACLUpdatedEventSerializationTest.java
+++ 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxACLUpdatedEventSerializationTest.java
@@ -34,6 +34,7 @@ import org.apache.james.mailbox.model.MailboxACL;
 import org.apache.james.mailbox.model.MailboxConstants;
 import org.apache.james.mailbox.model.MailboxPath;
 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;
 
@@ -44,7 +45,7 @@ class MailboxACLUpdatedEventSerializationTest {
     private static final User USER = User.fromUsername("user");
     private static final MailboxACL.EntryKey ENTRY_KEY = 
org.apache.james.mailbox.model.MailboxACL.EntryKey.createGroupEntryKey("any", 
false);
     private static final MailboxACL.Rfc4314Rights RIGHTS = new 
MailboxACL.Rfc4314Rights(MailboxACL.Right.Administer, MailboxACL.Right.Read);
-    private static final EventSerializer EVENT_SERIALIZER = new 
EventSerializer(new TestId.Factory());
+    private static final EventSerializer EVENT_SERIALIZER = new 
EventSerializer(new TestId.Factory(), new TestMessageId.Factory());
     private static final MailboxACL MAILBOX_ACL = new MailboxACL(
         new MailboxACL.Entry(ENTRY_KEY, RIGHTS),
         new MailboxACL.Entry(MailboxACL.EntryKey.createUserEntryKey("alice", 
true),

http://git-wip-us.apache.org/repos/asf/james-project/blob/39bc507a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxAddedSerializationTest.java
----------------------------------------------------------------------
diff --git 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxAddedSerializationTest.java
 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxAddedSerializationTest.java
index 0c7b3ea..339e4d6 100644
--- 
a/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxAddedSerializationTest.java
+++ 
b/mailbox/event/json/src/test/java/org/apache/james/event/json/MailboxAddedSerializationTest.java
@@ -31,6 +31,7 @@ import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.model.MailboxConstants;
 import org.apache.james.mailbox.model.MailboxPath;
 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;
 
@@ -38,7 +39,7 @@ class MailboxAddedSerializationTest {
 
     private static final User USER = User.fromUsername("user");
 
-    private static final EventSerializer EVENT_SERIALIZER = new 
EventSerializer(new TestId.Factory());
+    private static final EventSerializer EVENT_SERIALIZER = new 
EventSerializer(new TestId.Factory(), new TestMessageId.Factory());
 
     private static final MailboxListener.MailboxAdded EVENT_1 = new 
MailboxListener.MailboxAdded(
         MailboxSession.SessionId.of(42),


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to