This is an automated email from the ASF dual-hosted git repository.

matthieu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git


The following commit(s) were added to refs/heads/master by this push:
     new 5e69e0d  [Refactoring] use a strong type for IMAP MessageSequenceNumber
5e69e0d is described below

commit 5e69e0dbddbf788f9aee136596aecdcc024c9454
Author: Matthieu Baechler <matth...@apache.org>
AuthorDate: Mon Apr 20 17:23:26 2020 +0200

    [Refactoring] use a strong type for IMAP MessageSequenceNumber
---
 .../james/imap/api/display/HumanReadableText.java  |   5 +-
 .../imap/api/message/response/StatusResponse.java  |   5 +-
 .../james/imap/api/process/SelectedMailbox.java    |   9 +-
 .../james/imap/encode/ExpungeResponseEncoder.java  |   3 +-
 .../james/imap/encode/FetchResponseEncoder.java    |   5 +-
 .../imap/message/response/ExpungeResponse.java     |   7 +-
 .../james/imap/message/response/FetchResponse.java |   7 +-
 .../imap/processor/AbstractMailboxProcessor.java   |  56 ++++----
 .../imap/processor/AbstractSelectionProcessor.java |  12 +-
 .../james/imap/processor/SearchProcessor.java      |   7 +-
 .../james/imap/processor/StoreProcessor.java       |  82 +++++------
 .../imap/processor/base/SelectedMailboxImpl.java   |   9 +-
 .../james/imap/processor/base/UidMsnConverter.java |   7 +-
 .../imap/processor/fetch/FetchResponseBuilder.java | 150 ++++++++++-----------
 .../MessageSequenceNumber.java}                    |  43 ++++--
 .../mailbox/NullableMessageSequenceNumber.java     |  90 +++++++++++++
 .../encode/FetchResponseEncoderEnvelopeTest.java   |   3 +-
 .../FetchResponseEncoderNoExtensionsTest.java      |  10 +-
 .../imap/encode/FetchResponseEncoderTest.java      |   8 +-
 .../imap/processor/base/UidMsnConverterTest.java   |   8 +-
 .../james/mailbox/MessageSequenceNumberTest.java}  |  40 ++++--
 .../mailbox/NullableMessageSequenceNumberTest.java |  94 +++++++++++++
 22 files changed, 455 insertions(+), 205 deletions(-)

diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/api/display/HumanReadableText.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/api/display/HumanReadableText.java
index e231dd6..ac52c7d 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/api/display/HumanReadableText.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/api/display/HumanReadableText.java
@@ -24,6 +24,7 @@ import java.util.Arrays;
 import javax.mail.Flags;
 
 import org.apache.james.imap.api.ImapConstants;
+import org.apache.james.mailbox.MessageSequenceNumber;
 
 import com.google.common.base.Joiner;
 
@@ -36,8 +37,8 @@ public class HumanReadableText {
 
     public static final HumanReadableText SELECT = new 
HumanReadableText("org.apache.james.imap.SELECT", "completed.");
 
-    public static HumanReadableText unseen(long numberUnseen) {
-        return new HumanReadableText("org.apache.james.imap.UNSEEN", 
"MailboxMessage " + numberUnseen + " is first unseen");
+    public static HumanReadableText unseen(MessageSequenceNumber numberUnseen) 
{
+        return new HumanReadableText("org.apache.james.imap.UNSEEN", 
"MailboxMessage " + numberUnseen.asInt() + " is first unseen");
     }
 
     public static final HumanReadableText UIDNEXT = new 
HumanReadableText("org.apache.james.imap.UIDNEXT", "Predicted next UID");
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java
index 39ecc29..b2307cd 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java
@@ -34,6 +34,7 @@ import org.apache.james.imap.api.display.HumanReadableText;
 import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.MessageFlags;
 import org.apache.james.imap.api.message.UidRange;
+import org.apache.james.mailbox.MessageSequenceNumber;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.ModSeq;
 import org.apache.james.mailbox.model.UidValidity;
@@ -299,8 +300,8 @@ public interface StatusResponse extends ImapResponseMessage 
{
          *            positive non-zero integer
          * @return <code>ResponseCode</code>, not null
          */
-        public static ResponseCode unseen(int numberUnseen) {
-            return new ResponseCode("UNSEEN", numberUnseen);
+        public static ResponseCode unseen(MessageSequenceNumber numberUnseen) {
+            return new ResponseCode("UNSEEN", numberUnseen.asInt());
         }
 
         /**
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/api/process/SelectedMailbox.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/api/process/SelectedMailbox.java
index c16bdbc..8516109 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/api/process/SelectedMailbox.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/api/process/SelectedMailbox.java
@@ -25,6 +25,7 @@ import java.util.Optional;
 import javax.mail.Flags;
 
 import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.NullableMessageSequenceNumber;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxPath;
@@ -42,10 +43,10 @@ public interface SelectedMailbox {
     void deselect();
 
     /**
-     * Return the msg index of the given uid or {@link #NO_SUCH_MESSAGE} if no
+     * Return the msg index of the given uid or {@link 
NullableMessageSequenceNumber#noMessage()} instance if no
      * message with the given uid was found
      */
-    int msn(MessageUid uid);
+    NullableMessageSequenceNumber msn(MessageUid uid);
 
     /**
      * Return the uid of the message for the given index or empty if no 
message with the given index was found
@@ -144,10 +145,10 @@ public interface SelectedMailbox {
      * @param uid
      *            not null
      * @return the message sequence number that the UID held before or
-     *         {@link #NO_SUCH_MESSAGE} if no message with the given uid was
+     *         {@link NullableMessageSequenceNumber#noMessage()} instance if 
no message with the given uid was
      *         found being expunged
      */
-    int remove(MessageUid uid);
+    NullableMessageSequenceNumber remove(MessageUid uid);
 
     /**
      * Return a Collection which holds all uids reflecting the Messages which
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/encode/ExpungeResponseEncoder.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/encode/ExpungeResponseEncoder.java
index 992ee56..49a7c7e 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/encode/ExpungeResponseEncoder.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/encode/ExpungeResponseEncoder.java
@@ -25,6 +25,7 @@ import org.apache.james.imap.message.response.ExpungeResponse;
 
 public class ExpungeResponseEncoder implements 
ImapResponseEncoder<ExpungeResponse> {
     public static final String EXPUNGE = "EXPUNGE";
+    private static final int NO_MESSAGE = -1;
 
     @Override
     public Class<ExpungeResponse> acceptableMessages() {
@@ -33,7 +34,7 @@ public class ExpungeResponseEncoder implements 
ImapResponseEncoder<ExpungeRespon
 
     @Override
     public void encode(ExpungeResponse expungeResponse, ImapResponseComposer 
composer) throws IOException {
-        int messageSequenceNumber = expungeResponse.getMessageSequenceNumber();
+        int messageSequenceNumber = 
expungeResponse.getMessageSequenceNumber().asInt().orElse(NO_MESSAGE);
         
composer.untagged().message(messageSequenceNumber).message(EXPUNGE).end();
     }
 }
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/encode/FetchResponseEncoder.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/encode/FetchResponseEncoder.java
index 2849cda..9776f16 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/encode/FetchResponseEncoder.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/encode/FetchResponseEncoder.java
@@ -33,6 +33,7 @@ import javax.mail.Flags;
 import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.message.response.FetchResponse;
 import org.apache.james.imap.message.response.FetchResponse.Structure;
+import org.apache.james.mailbox.MessageSequenceNumber;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.ModSeq;
 import org.slf4j.Logger;
@@ -63,9 +64,9 @@ public class FetchResponseEncoder implements 
ImapResponseEncoder<FetchResponse>
 
     @Override
     public void encode(FetchResponse fetchResponse, ImapResponseComposer 
composer) throws IOException {
-        long messageNumber = fetchResponse.getMessageNumber();
+        MessageSequenceNumber messageNumber = fetchResponse.getMessageNumber();
 
-        
composer.untagged().message(messageNumber).message(ImapConstants.FETCH_COMMAND.getName()).openParen();
+        
composer.untagged().message(messageNumber.asInt()).message(ImapConstants.FETCH_COMMAND.getName()).openParen();
 
         encodeModSeq(composer, fetchResponse);
         encodeFlags(composer, fetchResponse);
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java
index 2d13203..2c46fce 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java
@@ -20,16 +20,17 @@
 package org.apache.james.imap.message.response;
 
 import org.apache.james.imap.api.message.response.ImapResponseMessage;
+import org.apache.james.mailbox.NullableMessageSequenceNumber;
 
 public final class ExpungeResponse implements ImapResponseMessage {
 
-    private final int messageSequenceNumber;
+    private final NullableMessageSequenceNumber messageSequenceNumber;
 
-    public ExpungeResponse(int messageSequenceNumber) {
+    public ExpungeResponse(NullableMessageSequenceNumber 
messageSequenceNumber) {
         this.messageSequenceNumber = messageSequenceNumber;
     }
 
-    public int getMessageSequenceNumber() {
+    public NullableMessageSequenceNumber getMessageSequenceNumber() {
         return messageSequenceNumber;
     }
 
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/message/response/FetchResponse.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/message/response/FetchResponse.java
index 0d64091..1b5133f 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/message/response/FetchResponse.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/message/response/FetchResponse.java
@@ -26,11 +26,12 @@ import java.util.Map;
 import javax.mail.Flags;
 
 import org.apache.james.imap.api.message.response.ImapResponseMessage;
+import org.apache.james.mailbox.MessageSequenceNumber;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.ModSeq;
 
 public final class FetchResponse implements ImapResponseMessage {
-    private final int messageNumber;
+    private final MessageSequenceNumber messageNumber;
     private final Flags flags;
     private final MessageUid uid;
     private final Date internalDate;
@@ -41,7 +42,7 @@ public final class FetchResponse implements 
ImapResponseMessage {
     private final Structure bodystructure;
     private final ModSeq modSeq;
 
-    public FetchResponse(int messageNumber, Flags flags, MessageUid uid, 
ModSeq modSeq, Date internalDate, Long size, Envelope envelope, Structure body, 
Structure bodystructure, List<BodyElement> elements) {
+    public FetchResponse(MessageSequenceNumber messageNumber, Flags flags, 
MessageUid uid, ModSeq modSeq, Date internalDate, Long size, Envelope envelope, 
Structure body, Structure bodystructure, List<BodyElement> elements) {
         this.messageNumber = messageNumber;
         this.flags = flags;
         this.uid = uid;
@@ -79,7 +80,7 @@ public final class FetchResponse implements 
ImapResponseMessage {
      * 
      * @return message number
      */
-    public int getMessageNumber() {
+    public MessageSequenceNumber getMessageNumber() {
         return messageNumber;
     }
 
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java
index f094609..8377e05 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java
@@ -57,6 +57,7 @@ import org.apache.james.mailbox.MessageManager;
 import org.apache.james.mailbox.MessageManager.MetaData;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.ModSeq;
+import org.apache.james.mailbox.NullableMessageSequenceNumber;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.exception.MessageRangeException;
 import org.apache.james.mailbox.model.FetchGroup;
@@ -178,7 +179,7 @@ public abstract class AbstractMailboxProcessor<R extends 
ImapRequest> extends Ab
             // we need to remove the message in the loop to the sequence 
numbers
             // are updated correctly.
             // See 7.4.1. EXPUNGE Response
-            final int msn = selected.remove(uid);
+            final NullableMessageSequenceNumber msn = selected.remove(uid);
             ExpungeResponse response = new ExpungeResponse(msn);
             responder.respond(response);
         }
@@ -245,37 +246,38 @@ public abstract class AbstractMailboxProcessor<R extends 
ImapRequest> extends Ab
         while (it.hasNext()) {
             MessageResult mr = it.next();
             final MessageUid uid = mr.getUid();
-            int msn = selected.msn(uid);
-            if (msn == SelectedMailbox.NO_SUCH_MESSAGE) {
+            selected.msn(uid).fold(() -> {
                 LOGGER.debug("No message found with uid {} in the uid<->msn 
mapping for mailbox {}. This may be because it was deleted by a concurrent 
session. So skip it..", uid, selected.getMailboxId().serialize());
                 // skip this as it was not found in the mapping
                 // 
                 // See IMAP-346
-                continue;
-            }
+                return null;
+            }, msn -> {
 
-            final Flags flags = mr.getFlags();
-            final MessageUid uidOut;
-            if (useUid || qresyncEnabled) {
-                uidOut = uid;
-            } else {
-                uidOut = null;
-            }
-            if (selected.isRecent(uid)) {
-                flags.add(Flags.Flag.RECENT);
-            } else {
-                flags.remove(Flags.Flag.RECENT);
-            }
-            final FetchResponse response;
-            
-            // Check if we also need to return the MODSEQ in the response. 
This is true if CONDSTORE or
-            // if QRESYNC was enabled, and the mailbox supports the permant 
storage of mod-sequences
-            if ((condstoreEnabled || qresyncEnabled) && isModSeqPermanent) {
-                response = new FetchResponse(msn, flags, uidOut, 
mr.getModSeq(), null, null, null, null, null, null);
-            } else {
-                response = new FetchResponse(msn, flags, uidOut, null, null, 
null, null, null, null, null);
-            }
-            responder.respond(response);
+                final Flags flags = mr.getFlags();
+                final MessageUid uidOut;
+                if (useUid || qresyncEnabled) {
+                    uidOut = uid;
+                } else {
+                    uidOut = null;
+                }
+                if (selected.isRecent(uid)) {
+                    flags.add(Flags.Flag.RECENT);
+                } else {
+                    flags.remove(Flags.Flag.RECENT);
+                }
+                final FetchResponse response;
+
+                // Check if we also need to return the MODSEQ in the response. 
This is true if CONDSTORE or
+                // if QRESYNC was enabled, and the mailbox supports the 
permant storage of mod-sequences
+                if ((condstoreEnabled || qresyncEnabled) && isModSeqPermanent) 
{
+                    response = new FetchResponse(msn, flags, uidOut, 
mr.getModSeq(), null, null, null, null, null, null);
+                } else {
+                    response = new FetchResponse(msn, flags, uidOut, null, 
null, null, null, null, null, null);
+                }
+                responder.respond(response);
+                return null;
+            });
         }
     }
 
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
index df3f61a..97d9055 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
@@ -349,15 +349,15 @@ abstract class AbstractSelectionProcessor<R extends 
AbstractMailboxSelectionRequ
     private boolean unseen(Responder responder, MessageUid firstUnseen, 
SelectedMailbox selected) throws MailboxException {
         if (firstUnseen != null) {
             final MessageUid unseenUid = firstUnseen;
-            int msn = selected.msn(unseenUid);
 
-            if (msn == SelectedMailbox.NO_SUCH_MESSAGE) {
+            return selected.msn(unseenUid).fold(() -> {
                 LOGGER.debug("No message found with uid {} in mailbox {}", 
unseenUid, selected.getMailboxId().serialize());
                 return false;
-            } 
-
-            final StatusResponse untaggedOk = 
statusResponseFactory.untaggedOk(HumanReadableText.unseen(msn), 
ResponseCode.unseen(msn));
-            responder.respond(untaggedOk);
+            }, msn -> {
+                final StatusResponse untaggedOk = 
statusResponseFactory.untaggedOk(HumanReadableText.unseen(msn), 
ResponseCode.unseen(msn));
+                responder.respond(untaggedOk);
+                return true;
+            });
         }
         return true;
 
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java
index 80b16ad..91d4ee4 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java
@@ -67,6 +67,7 @@ import org.apache.james.util.MDCBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.github.fge.lambdas.Throwing;
 import com.github.steveash.guavate.Guavate;
 import com.google.common.collect.ImmutableList;
 
@@ -212,8 +213,10 @@ public class SearchProcessor extends 
AbstractMailboxProcessor<SearchRequest> imp
         } else {
             return uids.stream()
                 .map(uid -> session.getSelected().msn(uid))
-                .map(Integer::longValue)
-                .filter(msn -> msn != SelectedMailbox.NO_SUCH_MESSAGE)
+                .flatMap(Throwing.function(nullableMsn ->
+                    nullableMsn.fold(
+                        Stream::empty,
+                        msn -> 
Stream.of(Integer.valueOf(msn.asInt()).longValue()))))
                 .collect(Guavate.toImmutableList());
         }
     }
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/StoreProcessor.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/StoreProcessor.java
index 79fe6fa..8c34775 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/StoreProcessor.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/StoreProcessor.java
@@ -49,6 +49,7 @@ import org.apache.james.mailbox.MessageManager;
 import org.apache.james.mailbox.MessageManager.MetaData;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.ModSeq;
+import org.apache.james.mailbox.NullableMessageSequenceNumber;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.exception.MessageRangeException;
 import org.apache.james.mailbox.model.FetchGroup;
@@ -111,7 +112,7 @@ public class StoreProcessor extends 
AbstractMailboxProcessor<StoreRequest> {
               
             } 
             final List<MessageUid> failed = new ArrayList<>();
-            List<Long> failedMsns = new ArrayList<>();
+            List<NullableMessageSequenceNumber> failedMsns = new ArrayList<>();
             final List<String> userFlags = Arrays.asList(flags.getUserFlags());
             for (IdRange range : idSet) {
                 final SelectedMailbox selected = session.getSelected();
@@ -157,7 +158,7 @@ public class StoreProcessor extends 
AbstractMailboxProcessor<StoreRequest> {
                                 if (useUids) {
                                     failed.add(uid);
                                 } else {
-                                    failedMsns.add((long)selected.msn(uid));
+                                    failedMsns.add(selected.msn(uid));
                                 }
                             }
                         }
@@ -198,8 +199,8 @@ public class StoreProcessor extends 
AbstractMailboxProcessor<StoreRequest> {
                     responder.respond(response);
                 } else {
                     List<IdRange> ranges = new ArrayList<>();
-                    for (long msn: failedMsns) {
-                        ranges.add(new IdRange(msn));
+                    for (NullableMessageSequenceNumber msn: failedMsns) {
+                        msn.ifPresent(id -> ranges.add(new 
IdRange(id.asInt())));
                     }
                     IdRange[] failedRanges = 
IdRange.mergeRanges(ranges).toArray(IdRange[]::new);
                     // See RFC4551 3.2. STORE and UID STORE Commands
@@ -264,50 +265,51 @@ public class StoreProcessor extends 
AbstractMailboxProcessor<StoreRequest> {
             
             for (Map.Entry<MessageUid, Flags> entry : flagsByUid.entrySet()) {
                 final MessageUid uid = entry.getKey();
-                final int msn = selected.msn(uid);
 
-                if (msn == SelectedMailbox.NO_SUCH_MESSAGE) {
+                selected.msn(uid).fold(() -> {
                     LOGGER.debug("No message found with uid {} in the 
uid<->msn mapping for mailbox {}. This may be because it was deleted by a 
concurrent session. So skip it..", uid, selected.getPath().asString());
                     // skip this as it was not found in the mapping
                     // 
                     // See IMAP-346
-                    continue;
-                }
+                    return null;
+                }, msn -> {
 
-                final Flags resultFlags = entry.getValue();
-                final MessageUid resultUid;
-                
-                // Check if we need to include the uid. T
-                //
-                // This is the case if one of these is true:
-                //      - FETCH (UID...)  was used
-                //      - QRESYNC was enabled via ENABLE QRESYNC
-                if (useUids || qresyncEnabled) {
-                    resultUid = uid;
-                } else {
-                    resultUid = null;
-                }
+                    final Flags resultFlags = entry.getValue();
+                    final MessageUid resultUid;
 
-                if (selected.isRecent(uid)) {
-                    resultFlags.add(Flags.Flag.RECENT);
-                }
-               
-                final FetchResponse response;
-                // For more information related to the FETCH response see
-                //
-                // RFC4551 3.2. STORE and UID STORE Commands
-                if (silent && (unchangedSince != -1 || qresyncEnabled || 
condstoreEnabled)) {
-                    // We need to return an FETCH response which contains the 
mod-sequence of the message even if FLAGS.SILENT was used
-                    response = new FetchResponse(msn, null, resultUid, 
modSeqs.get(uid), null, null, null, null, null, null);
-                } else if (!silent && (unchangedSince != -1 || qresyncEnabled 
|| condstoreEnabled)) {
+                    // Check if we need to include the uid. T
                     //
-                    // Use a FETCH response which contains the mod-sequence 
and the flags
-                    response = new FetchResponse(msn, resultFlags, resultUid, 
modSeqs.get(uid), null, null, null, null, null, null);
-                } else {
-                    // Use a FETCH response which only contains the flags as 
no CONDSTORE was used
-                    response = new FetchResponse(msn, resultFlags, resultUid, 
null, null, null, null, null, null, null);
-                }
-                responder.respond(response);
+                    // This is the case if one of these is true:
+                    //      - FETCH (UID...)  was used
+                    //      - QRESYNC was enabled via ENABLE QRESYNC
+                    if (useUids || qresyncEnabled) {
+                        resultUid = uid;
+                    } else {
+                        resultUid = null;
+                    }
+
+                    if (selected.isRecent(uid)) {
+                        resultFlags.add(Flags.Flag.RECENT);
+                    }
+
+                    final FetchResponse response;
+                    // For more information related to the FETCH response see
+                    //
+                    // RFC4551 3.2. STORE and UID STORE Commands
+                    if (silent && (unchangedSince != -1 || qresyncEnabled || 
condstoreEnabled)) {
+                        // We need to return an FETCH response which contains 
the mod-sequence of the message even if FLAGS.SILENT was used
+                        response = new FetchResponse(msn, null, resultUid, 
modSeqs.get(uid), null, null, null, null, null, null);
+                    } else if (!silent && (unchangedSince != -1 || 
qresyncEnabled || condstoreEnabled)) {
+                        //
+                        // Use a FETCH response which contains the 
mod-sequence and the flags
+                        response = new FetchResponse(msn, resultFlags, 
resultUid, modSeqs.get(uid), null, null, null, null, null, null);
+                    } else {
+                        // Use a FETCH response which only contains the flags 
as no CONDSTORE was used
+                        response = new FetchResponse(msn, resultFlags, 
resultUid, null, null, null, null, null, null, null);
+                    }
+                    responder.respond(response);
+                    return null;
+                });
             }
 
             if (unchangedSince != -1) {
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java
index a8ca86f..1aef2bf 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java
@@ -38,6 +38,7 @@ import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageManager;
 import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.NullableMessageSequenceNumber;
 import org.apache.james.mailbox.events.Event;
 import org.apache.james.mailbox.events.EventBus;
 import org.apache.james.mailbox.events.MailboxIdRegistrationKey;
@@ -183,8 +184,8 @@ public class SelectedMailboxImpl implements 
SelectedMailbox, MailboxListener {
     }
 
     @Override
-    public synchronized  int remove(MessageUid uid) {
-        final int result = msn(uid);
+    public synchronized NullableMessageSequenceNumber remove(MessageUid uid) {
+        NullableMessageSequenceNumber result = msn(uid);
         uidMsnConverter.remove(uid);
         return result;
     }
@@ -391,8 +392,8 @@ public class SelectedMailboxImpl implements 
SelectedMailbox, MailboxListener {
     }
 
     @Override
-    public synchronized int msn(MessageUid uid) {
-        return uidMsnConverter.getMsn(uid).orElse(NO_SUCH_MESSAGE);
+    public synchronized NullableMessageSequenceNumber msn(MessageUid uid) {
+        return uidMsnConverter.getMsn(uid);
     }
 
     @Override
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/base/UidMsnConverter.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/base/UidMsnConverter.java
index ff56229..fc7b114 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/base/UidMsnConverter.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/base/UidMsnConverter.java
@@ -26,6 +26,7 @@ import java.util.Optional;
 import java.util.TreeSet;
 
 import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.NullableMessageSequenceNumber;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Lists;
@@ -48,12 +49,12 @@ public class UidMsnConverter {
         uids.addAll(tmp);
     }
 
-    public synchronized Optional<Integer> getMsn(MessageUid uid) {
+    public synchronized NullableMessageSequenceNumber getMsn(MessageUid uid) {
         int position = Collections.binarySearch(uids, uid);
         if (position < 0) {
-            return Optional.empty();
+            return NullableMessageSequenceNumber.noMessage();
         }
-        return Optional.of(position + 1);
+        return NullableMessageSequenceNumber.of(position + 1);
     }
 
     public synchronized Optional<MessageUid> getUid(int msn) {
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/fetch/FetchResponseBuilder.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/fetch/FetchResponseBuilder.java
index 40cd7f6..4229b5e 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/fetch/FetchResponseBuilder.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/fetch/FetchResponseBuilder.java
@@ -41,6 +41,7 @@ import org.apache.james.imap.api.process.SelectedMailbox;
 import org.apache.james.imap.message.response.FetchResponse;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.MessageSequenceNumber;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.ModSeq;
 import org.apache.james.mailbox.exception.MailboxException;
@@ -55,7 +56,7 @@ public final class FetchResponseBuilder {
 
     private final EnvelopeBuilder envelopeBuilder;
 
-    private int msn;
+    private MessageSequenceNumber msn;
 
     private MessageUid uid;
 
@@ -80,7 +81,7 @@ public final class FetchResponseBuilder {
         this.envelopeBuilder = envelopeBuilder;
     }
 
-    public void reset(int msn) {
+    public void reset(MessageSequenceNumber msn) {
         this.msn = msn;
         uid = null;
         flags = null;
@@ -112,95 +113,94 @@ public final class FetchResponseBuilder {
     public FetchResponse build(FetchData fetch, MessageResult result, 
MessageManager mailbox, ImapSession session, boolean useUids) throws 
MessageRangeException, MailboxException {
         final SelectedMailbox selected = session.getSelected();
         final MessageUid resultUid = result.getUid();
-        final int resultMsn = selected.msn(resultUid);
-
-        if (resultMsn == SelectedMailbox.NO_SUCH_MESSAGE) {
+        return selected.msn(resultUid).fold(() -> {
             throw new MessageRangeException("No such message found with uid " 
+ resultUid);
-        }
+        }, msn -> {
+
+            reset(msn);
+            // setMsn(resultMsn);
+
+            // Check if this fetch will cause the "SEEN" flag to be set on this
+            // message. If so, update the flags, and ensure that a flags 
response is
+            // included in the response.
+            final MailboxSession mailboxSession = session.getMailboxSession();
+            boolean ensureFlagsResponse = false;
+            final Flags resultFlags = result.getFlags();
+            if (fetch.isSetSeen() && !resultFlags.contains(Flags.Flag.SEEN)) {
+                mailbox.setFlags(new Flags(Flags.Flag.SEEN), 
MessageManager.FlagsUpdateMode.ADD, MessageRange.one(resultUid), 
mailboxSession);
+                resultFlags.add(Flags.Flag.SEEN);
+                ensureFlagsResponse = true;
+            }
 
-        reset(resultMsn);
-        // setMsn(resultMsn);
-
-        // Check if this fetch will cause the "SEEN" flag to be set on this
-        // message. If so, update the flags, and ensure that a flags response 
is
-        // included in the response.
-        final MailboxSession mailboxSession = session.getMailboxSession();
-        boolean ensureFlagsResponse = false;
-        final Flags resultFlags = result.getFlags();
-        if (fetch.isSetSeen() && !resultFlags.contains(Flags.Flag.SEEN)) {
-            mailbox.setFlags(new Flags(Flags.Flag.SEEN), 
MessageManager.FlagsUpdateMode.ADD, MessageRange.one(resultUid), 
mailboxSession);
-            resultFlags.add(Flags.Flag.SEEN);
-            ensureFlagsResponse = true;
-        }
+            // FLAGS response
+            if (fetch.contains(Item.FLAGS) || ensureFlagsResponse) {
+                if (selected.isRecent(resultUid)) {
+                    resultFlags.add(Flags.Flag.RECENT);
+                }
+                setFlags(resultFlags);
+            }
 
-        // FLAGS response
-        if (fetch.contains(Item.FLAGS) || ensureFlagsResponse) {
-            if (selected.isRecent(resultUid)) {
-                resultFlags.add(Flags.Flag.RECENT);
+            // INTERNALDATE response
+            if (fetch.contains(Item.INTERNAL_DATE)) {
+                setInternalDate(result.getInternalDate());
             }
-            setFlags(resultFlags);
-        }
 
-        // INTERNALDATE response
-        if (fetch.contains(Item.INTERNAL_DATE)) {
-            setInternalDate(result.getInternalDate());
-        }
+            // RFC822.SIZE response
+            if (fetch.contains(Item.SIZE)) {
+                setSize(result.getSize());
+            }
 
-        // RFC822.SIZE response
-        if (fetch.contains(Item.SIZE)) {
-            setSize(result.getSize());
-        }
+            if (fetch.contains(Item.ENVELOPE)) {
+                this.envelope = buildEnvelope(result);
+            }
 
-        if (fetch.contains(Item.ENVELOPE)) {
-            this.envelope = buildEnvelope(result);
-        }
 
+            // BODY part responses.
+            Collection<BodyFetchElement> elements = fetch.getBodyElements();
+            this.elements = new ArrayList<>();
+            for (BodyFetchElement fetchElement : elements) {
+                final FetchResponse.BodyElement element = bodyFetch(result, 
fetchElement);
+                if (element != null) {
+                    this.elements.add(element);
+                }
+            }
+
+            // Only create when needed
+            if (fetch.contains(Item.BODY) || 
fetch.contains(Item.BODY_STRUCTURE)) {
+                // BODY response
+                //
+                // the STRUCTURE is only needed when no specific element is 
requested otherwise we don't need
+                // to access it and may be able to not parse the message
+                //
+                // See IMAP-333
+                if (fetch.contains(Item.BODY) && this.elements.isEmpty()) {
+                    body = new MimeDescriptorStructure(false, 
result.getMimeDescriptor(), envelopeBuilder);
+                }
 
-        // BODY part responses.
-        Collection<BodyFetchElement> elements = fetch.getBodyElements();
-        this.elements = new ArrayList<>();
-        for (BodyFetchElement fetchElement : elements) {
-            final FetchResponse.BodyElement element = bodyFetch(result, 
fetchElement);
-            if (element != null) {
-                this.elements.add(element);
+                // BODYSTRUCTURE response
+                if (fetch.contains(Item.BODY_STRUCTURE)) {
+                    bodystructure = new MimeDescriptorStructure(true, 
result.getMimeDescriptor(), envelopeBuilder);
+                }
             }
-        }
-        
-        // Only create when needed
-        if (fetch.contains(Item.BODY) || fetch.contains(Item.BODY_STRUCTURE)) {
-            // BODY response
-            //
-            // the STRUCTURE is only needed when no specific element is 
requested otherwise we don't need 
-            // to access it and may be able to not parse the message
-            //
-            // See IMAP-333
-            if (fetch.contains(Item.BODY) && this.elements.isEmpty()) {
-                body = new MimeDescriptorStructure(false, 
result.getMimeDescriptor(), envelopeBuilder);
+            // UID response
+            if (fetch.contains(Item.UID)) {
+                setUid(resultUid);
             }
 
-            // BODYSTRUCTURE response
-            if (fetch.contains(Item.BODY_STRUCTURE)) {
-                bodystructure = new MimeDescriptorStructure(true, 
result.getMimeDescriptor(), envelopeBuilder);
-            }
-        }
-        // UID response
-        if (fetch.contains(Item.UID)) {
-            setUid(resultUid);
-        }
 
-        
-        if (fetch.contains(Item.MODSEQ)) {
-            long changedSince = fetch.getChangedSince();
-            if (changedSince != -1) {
-                // check if the modsequence if higher then the one specified 
by the CHANGEDSINCE option
-                if (changedSince < result.getModSeq().asLong()) {
+            if (fetch.contains(Item.MODSEQ)) {
+                long changedSince = fetch.getChangedSince();
+                if (changedSince != -1) {
+                    // check if the modsequence if higher then the one 
specified by the CHANGEDSINCE option
+                    if (changedSince < result.getModSeq().asLong()) {
+                        setModSeq(result.getModSeq());
+                    }
+                } else {
                     setModSeq(result.getModSeq());
                 }
-            } else {
-                setModSeq(result.getModSeq());
             }
-        }
-        return build();
+            return build();
+        });
     }
 
     private FetchResponse.Envelope buildEnvelope(MessageResult result) throws 
MailboxException {
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java
 
b/protocols/imap/src/main/java/org/apache/james/mailbox/MessageSequenceNumber.java
similarity index 56%
copy from 
protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java
copy to 
protocols/imap/src/main/java/org/apache/james/mailbox/MessageSequenceNumber.java
index 2d13203..df1c829 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/mailbox/MessageSequenceNumber.java
@@ -17,23 +17,48 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.imap.message.response;
+package org.apache.james.mailbox;
 
-import org.apache.james.imap.api.message.response.ImapResponseMessage;
+import java.util.Objects;
 
-public final class ExpungeResponse implements ImapResponseMessage {
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
 
-    private final int messageSequenceNumber;
+public final class MessageSequenceNumber {
 
-    public ExpungeResponse(int messageSequenceNumber) {
-        this.messageSequenceNumber = messageSequenceNumber;
+    public static MessageSequenceNumber of(int msn) {
+        Preconditions.checkArgument(msn >= 0);
+        return new MessageSequenceNumber(msn);
     }
 
-    public int getMessageSequenceNumber() {
-        return messageSequenceNumber;
+    private final int msn;
+
+    private MessageSequenceNumber(int msn) {
+        this.msn = msn;
+    }
+
+    public int asInt() {
+        return msn;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof MessageSequenceNumber) {
+            MessageSequenceNumber that = (MessageSequenceNumber) o;
+            return Objects.equals(msn, that.msn);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(msn);
     }
 
+    @Override
     public String toString() {
-        return "EXPUNGE " + messageSequenceNumber;
+        return MoreObjects.toStringHelper(this)
+            .add("msn", msn)
+            .toString();
     }
 }
diff --git 
a/protocols/imap/src/main/java/org/apache/james/mailbox/NullableMessageSequenceNumber.java
 
b/protocols/imap/src/main/java/org/apache/james/mailbox/NullableMessageSequenceNumber.java
new file mode 100644
index 0000000..75d51bd
--- /dev/null
+++ 
b/protocols/imap/src/main/java/org/apache/james/mailbox/NullableMessageSequenceNumber.java
@@ -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.mailbox;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Consumer;
+
+import org.apache.james.mailbox.exception.MailboxException;
+
+import com.google.common.base.MoreObjects;
+
+public final class NullableMessageSequenceNumber {
+
+    public interface HandleNoMessage<T> {
+        T handle() throws MailboxException;
+    }
+
+    public interface HandleMessage<T> {
+        T handle(MessageSequenceNumber sequenceNumber) throws MailboxException;
+    }
+
+    public static NullableMessageSequenceNumber noMessage() {
+        return new NullableMessageSequenceNumber(Optional.empty());
+    }
+
+    public static NullableMessageSequenceNumber of(int msn) {
+        return new 
NullableMessageSequenceNumber(Optional.of(MessageSequenceNumber.of(msn)));
+    }
+
+    private final Optional<MessageSequenceNumber> msn;
+
+    private NullableMessageSequenceNumber(Optional<MessageSequenceNumber> msn) 
{
+        this.msn = msn;
+    }
+
+    public void ifPresent(Consumer<MessageSequenceNumber> consumer) {
+        msn.ifPresent(consumer);
+    }
+
+    public <T> T fold(HandleNoMessage<T> handleNoMessage, HandleMessage<T> 
handleMessage) throws MailboxException {
+        if (msn.isPresent()) {
+            return handleMessage.handle(msn.get());
+        } else {
+            return handleNoMessage.handle();
+        }
+    }
+
+    public Optional<Integer> asInt() {
+        return msn.map(MessageSequenceNumber::asInt);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof NullableMessageSequenceNumber) {
+            NullableMessageSequenceNumber that = 
(NullableMessageSequenceNumber) o;
+            return Objects.equals(msn, that.msn);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(msn);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("msn", msn)
+            .toString();
+    }
+}
diff --git 
a/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderEnvelopeTest.java
 
b/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderEnvelopeTest.java
index 340b8ce..281afc6 100644
--- 
a/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderEnvelopeTest.java
+++ 
b/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderEnvelopeTest.java
@@ -27,6 +27,7 @@ import 
org.apache.james.imap.encode.base.ByteImapResponseWriter;
 import org.apache.james.imap.encode.base.ImapResponseComposerImpl;
 import org.apache.james.imap.message.response.FetchResponse;
 import org.apache.james.imap.message.response.FetchResponse.Envelope.Address;
+import org.apache.james.mailbox.MessageSequenceNumber;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -49,7 +50,7 @@ public class FetchResponseEncoderEnvelopeTest {
 
     private static final String ADDRESS_TWO_NAME = "2NAME";
 
-    private static final int MSN = 100;
+    private static final MessageSequenceNumber MSN = 
MessageSequenceNumber.of(100);
 
     private FetchResponseEncoder encoder;
 
diff --git 
a/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderNoExtensionsTest.java
 
b/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderNoExtensionsTest.java
index a805a9c..40face7 100644
--- 
a/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderNoExtensionsTest.java
+++ 
b/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderNoExtensionsTest.java
@@ -33,11 +33,13 @@ import javax.mail.Flags;
 import org.apache.james.imap.encode.base.ByteImapResponseWriter;
 import org.apache.james.imap.encode.base.ImapResponseComposerImpl;
 import org.apache.james.imap.message.response.FetchResponse;
+import org.apache.james.mailbox.MessageSequenceNumber;
 import org.apache.james.mailbox.MessageUid;
 import org.junit.Before;
 import org.junit.Test;
 
 public class FetchResponseEncoderNoExtensionsTest {
+    private static final MessageSequenceNumber MSN = 
MessageSequenceNumber.of(100);
     private ByteImapResponseWriter writer = new ByteImapResponseWriter();
     private ImapResponseComposer composer = new 
ImapResponseComposerImpl(writer);
     private Flags flags;
@@ -55,7 +57,7 @@ public class FetchResponseEncoderNoExtensionsTest {
 
     @Test
     public void testShouldEncodeFlagsResponse() throws Exception {
-        FetchResponse message = new FetchResponse(100, flags, null, null, 
null, null,
+        FetchResponse message = new FetchResponse(MSN, flags, null, null, 
null, null,
                 null, null, null, null);
         encoder.encode(message, composer);
         assertThat(writer.getString()).isEqualTo("* 100 FETCH (FLAGS 
(\\Deleted))\r\n");
@@ -63,7 +65,7 @@ public class FetchResponseEncoderNoExtensionsTest {
 
     @Test
     public void testShouldEncodeUidResponse() throws Exception {
-        FetchResponse message = new FetchResponse(100, null, 
MessageUid.of(72), null,
+        FetchResponse message = new FetchResponse(MSN, null, 
MessageUid.of(72), null,
                 null, null, null, null, null, null);
         encoder.encode(message, composer);
         assertThat(writer.getString()).isEqualTo("* 100 FETCH (UID 72)\r\n");
@@ -72,7 +74,7 @@ public class FetchResponseEncoderNoExtensionsTest {
 
     @Test
     public void testShouldEncodeAllResponse() throws Exception {
-        FetchResponse message = new FetchResponse(100, flags, 
MessageUid.of(72), null,
+        FetchResponse message = new FetchResponse(MSN, flags, 
MessageUid.of(72), null,
                 null, null, null, null, null, null);
         encoder.encode(message, composer);
         assertThat(writer.getString()).isEqualTo("* 100 FETCH (FLAGS 
(\\Deleted) UID 72)\r\n");
@@ -81,7 +83,7 @@ public class FetchResponseEncoderNoExtensionsTest {
 
     @Test
     public void testShouldNotAddExtensionsWithEncodingBodyStructure() throws 
Exception {
-        FetchResponse message = new FetchResponse(100, flags, 
MessageUid.of(72), null,
+        FetchResponse message = new FetchResponse(MSN, flags, 
MessageUid.of(72), null,
                 null, null, null, null, stubStructure, null);
         final Map<String, String> parameters = new HashMap<>();
         parameters.put("CHARSET", "US-ASCII");
diff --git 
a/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderTest.java
 
b/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderTest.java
index 3a56fb6..5447cd0 100644
--- 
a/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderTest.java
+++ 
b/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderTest.java
@@ -26,11 +26,13 @@ import javax.mail.Flags;
 import org.apache.james.imap.encode.base.ByteImapResponseWriter;
 import org.apache.james.imap.encode.base.ImapResponseComposerImpl;
 import org.apache.james.imap.message.response.FetchResponse;
+import org.apache.james.mailbox.MessageSequenceNumber;
 import org.apache.james.mailbox.MessageUid;
 import org.junit.Before;
 import org.junit.Test;
 
 public class FetchResponseEncoderTest  {
+    private static final MessageSequenceNumber MSN = 
MessageSequenceNumber.of(100);
     private ByteImapResponseWriter writer = new ByteImapResponseWriter();
     private ImapResponseComposer composer = new 
ImapResponseComposerImpl(writer);
     private Flags flags;
@@ -49,7 +51,7 @@ public class FetchResponseEncoderTest  {
 
     @Test
     public void testShouldEncodeFlagsResponse() throws Exception {
-        FetchResponse message = new FetchResponse(100, flags, null, null, 
null, null,
+        FetchResponse message = new FetchResponse(MSN, flags, null, null, 
null, null,
                 null, null, null, null);
         encoder.encode(message, composer);
         assertThat(writer.getString()).isEqualTo("* 100 FETCH (FLAGS 
(\\Deleted))\r\n");
@@ -59,7 +61,7 @@ public class FetchResponseEncoderTest  {
 
     @Test
     public void testShouldEncodeUidResponse() throws Exception {
-        FetchResponse message = new FetchResponse(100, null, 
MessageUid.of(72), null,
+        FetchResponse message = new FetchResponse(MSN, null, 
MessageUid.of(72), null,
                 null, null, null, null, null, null); 
         encoder.encode(message, composer);
         assertThat(writer.getString()).isEqualTo("* 100 FETCH (UID 72)\r\n");
@@ -69,7 +71,7 @@ public class FetchResponseEncoderTest  {
 
     @Test
     public void testShouldEncodeAllResponse() throws Exception {
-        FetchResponse message = new FetchResponse(100, flags, 
MessageUid.of(72), null,
+        FetchResponse message = new FetchResponse(MSN, flags, 
MessageUid.of(72), null,
                 null, null, null, null, null, null);
         encoder.encode(message, composer);
         assertThat(writer.getString()).isEqualTo("* 100 FETCH (FLAGS 
(\\Deleted) UID 72)\r\n");
diff --git 
a/protocols/imap/src/test/java/org/apache/james/imap/processor/base/UidMsnConverterTest.java
 
b/protocols/imap/src/test/java/org/apache/james/imap/processor/base/UidMsnConverterTest.java
index 3c7c66c..7976841 100644
--- 
a/protocols/imap/src/test/java/org/apache/james/imap/processor/base/UidMsnConverterTest.java
+++ 
b/protocols/imap/src/test/java/org/apache/james/imap/processor/base/UidMsnConverterTest.java
@@ -24,6 +24,8 @@ import static org.assertj.core.api.Assertions.assertThat;
 import java.time.Duration;
 import java.util.Map;
 
+import org.apache.james.mailbox.MessageSequenceNumber;
+import org.apache.james.mailbox.NullableMessageSequenceNumber;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.util.concurrency.ConcurrentTestRunner;
 import org.junit.Before;
@@ -98,7 +100,7 @@ public class UidMsnConverterTest {
     public void getMsnShouldReturnAbsentIfNoCorrespondingMessage() {
         testee.addUid(messageUid1);
 
-        assertThat(testee.getMsn(messageUid2)).isEmpty();
+        
assertThat(testee.getMsn(messageUid2)).isEqualTo(NullableMessageSequenceNumber.noMessage());
     }
 
     @Test
@@ -107,7 +109,7 @@ public class UidMsnConverterTest {
         testee.addUid(messageUid2);
 
         assertThat(testee.getMsn(messageUid2))
-            .contains(2);
+            .isEqualTo(NullableMessageSequenceNumber.of(2));
     }
 
     @Test
@@ -173,7 +175,7 @@ public class UidMsnConverterTest {
         testee.addUid(messageUid2);
 
         assertThat(testee.getMsn(messageUid2))
-            .contains(2);
+            .isEqualTo(NullableMessageSequenceNumber.of(2));
     }
 
     @Test
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java
 
b/protocols/imap/src/test/java/org/apache/james/mailbox/MessageSequenceNumberTest.java
similarity index 54%
copy from 
protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java
copy to 
protocols/imap/src/test/java/org/apache/james/mailbox/MessageSequenceNumberTest.java
index 2d13203..16369cf 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java
+++ 
b/protocols/imap/src/test/java/org/apache/james/mailbox/MessageSequenceNumberTest.java
@@ -17,23 +17,41 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.imap.message.response;
+package org.apache.james.mailbox;
 
-import org.apache.james.imap.api.message.response.ImapResponseMessage;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
-public final class ExpungeResponse implements ImapResponseMessage {
+import org.junit.jupiter.api.Test;
 
-    private final int messageSequenceNumber;
+import nl.jqno.equalsverifier.EqualsVerifier;
 
-    public ExpungeResponse(int messageSequenceNumber) {
-        this.messageSequenceNumber = messageSequenceNumber;
+class MessageSequenceNumberTest {
+
+    @Test
+    void ofShouldThrowOnNegative() {
+        assertThatThrownBy(() -> 
MessageSequenceNumber.of(-1)).isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void ofShouldNotThrowOnZero() {
+        assertThatCode(() -> 
MessageSequenceNumber.of(0)).doesNotThrowAnyException();
     }
 
-    public int getMessageSequenceNumber() {
-        return messageSequenceNumber;
+    @Test
+    void ofShouldNotThrowOnPositiveValue() {
+        assertThatCode(() -> 
MessageSequenceNumber.of(12)).doesNotThrowAnyException();
     }
 
-    public String toString() {
-        return "EXPUNGE " + messageSequenceNumber;
+    @Test
+    void asIntShouldReturnValue() {
+        assertThat(MessageSequenceNumber.of(12).asInt()).isEqualTo(12);
     }
-}
+
+    @Test
+    void shouldRespectBeanContract() {
+        EqualsVerifier.forClass(MessageSequenceNumber.class).verify();
+    }
+
+}
\ No newline at end of file
diff --git 
a/protocols/imap/src/test/java/org/apache/james/mailbox/NullableMessageSequenceNumberTest.java
 
b/protocols/imap/src/test/java/org/apache/james/mailbox/NullableMessageSequenceNumberTest.java
new file mode 100644
index 0000000..9cb57b5
--- /dev/null
+++ 
b/protocols/imap/src/test/java/org/apache/james/mailbox/NullableMessageSequenceNumberTest.java
@@ -0,0 +1,94 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class NullableMessageSequenceNumberTest {
+
+    @Test
+    void ofShouldThrowOnNegative() {
+        assertThatThrownBy(() -> 
NullableMessageSequenceNumber.of(-1)).isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void ofShouldNotThrowOnZero() {
+        assertThatCode(() -> 
NullableMessageSequenceNumber.of(0)).doesNotThrowAnyException();
+    }
+
+    @Test
+    void ofShouldNotThrowOnPositiveValue() {
+        assertThatCode(() -> 
NullableMessageSequenceNumber.of(12)).doesNotThrowAnyException();
+    }
+
+    @Test
+    void asIntShouldReturnIntWhenMessage() {
+        assertThat(NullableMessageSequenceNumber.of(12).asInt()).contains(12);
+    }
+
+    @Test
+    void asIntShouldReturnEmptyWhenNoMessage() {
+        
assertThat(NullableMessageSequenceNumber.noMessage().asInt()).isEmpty();
+    }
+
+    @Test
+    void foldShouldCallOnlyFirstMethodOnNoMessage() {
+        assertThatCode(
+            () -> NullableMessageSequenceNumber.noMessage().fold(() -> 12, msn 
-> fail()))
+            .doesNotThrowAnyException();
+    }
+
+    @Test
+    void foldShouldCallOnlySecondMethodOnMessage() {
+        assertThatCode(
+            () -> NullableMessageSequenceNumber.of(24).fold(Assertions::fail, 
msn -> 12))
+            .doesNotThrowAnyException();
+    }
+
+    @Test
+    void ifPresentShouldNotCallMethodOnNoMessage() {
+        assertThatCode(
+            () -> NullableMessageSequenceNumber.noMessage().ifPresent(ignored 
-> Assertions.fail()))
+            .doesNotThrowAnyException();
+    }
+
+    @Test
+    void ifPresentShouldCallMethodOnWithMessage() {
+        AtomicReference<MessageSequenceNumber> ref = new AtomicReference<>();
+        NullableMessageSequenceNumber.of(24).ifPresent(ref::set);
+        assertThat(ref.get()).isNotNull();
+    }
+
+    @Test
+    void shouldRespectBeanContract() {
+        EqualsVerifier.forClass(NullableMessageSequenceNumber.class).verify();
+    }
+
+}
\ No newline at end of file


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

Reply via email to