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

[22/27] james-project git commit: JAMES-2132 Provide state of the art beans

JAMES-2132 Provide state of the art beans

 - toString, equals and hashcode
 - builder test
 - Edge case of constructors tested
 - Display of each field tested
 - value -> enum tested


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

Branch: refs/heads/master
Commit: 4020b75031f487d54e8bc13d9aa879b43b63164c
Parents: 879fe83
Author: benwa <bt...@linagora.com>
Authored: Tue Aug 29 14:09:08 2017 +0700
Committer: benwa <bt...@linagora.com>
Committed: Fri Sep 1 08:16:10 2017 +0700

----------------------------------------------------------------------
 mdn/pom.xml                                     |   8 +-
 .../java/org/apache/james/mdn/MDNReport.java    |  33 +++-
 .../apache/james/mdn/fields/Disposition.java    |  28 ++-
 .../java/org/apache/james/mdn/fields/Error.java |  25 +++
 .../apache/james/mdn/fields/ExtensionField.java |  29 +++
 .../apache/james/mdn/fields/FinalRecipient.java |  25 +++
 .../org/apache/james/mdn/fields/Gateway.java    |  31 +++-
 .../james/mdn/fields/OriginalMessageId.java     |  27 +++
 .../james/mdn/fields/OriginalRecipient.java     |  25 ++-
 .../james/mdn/fields/ReportingUserAgent.java    |  35 +++-
 .../java/org/apache/james/mdn/fields/Text.java  |  18 ++
 .../james/mdn/modifier/DispositionModifier.java |  38 +++-
 .../org/apache/james/mdn/MDNFactoryTest.java    |  90 +++-------
 .../org/apache/james/mdn/MDNReportTest.java     | 150 ++++++++++++++++
 .../action/mode/DispositionActionModeTest.java  |  49 +++++
 .../james/mdn/fields/DispositionTest.java       | 177 +++++++++++++++++++
 .../org/apache/james/mdn/fields/ErrorTest.java  |  62 +++++++
 .../james/mdn/fields/ExtensionFieldTest.java    |  71 ++++++++
 .../james/mdn/fields/FinalRecipientTest.java    | 101 +++++++++++
 .../apache/james/mdn/fields/GatewayTest.java    | 102 +++++++++++
 .../james/mdn/fields/OriginalMessageIdTest.java |  62 +++++++
 .../james/mdn/fields/OriginalRecipientTest.java |  95 ++++++++++
 .../mdn/fields/ReportingUserAgentTest.java      |  93 ++++++++++
 .../org/apache/james/mdn/fields/TextTest.java   |  97 ++++++++++
 .../mdn/modifier/DispositionModifierTest.java   |  53 ++++++
 .../mode/DispositionSendingModeTest.java        |  49 +++++
 .../james/mdn/type/DispositionTypeTest.java     |  62 +++++++
 .../transport/mailets/jsieve/RejectAction.java  |   2 +-
 28 files changed, 1556 insertions(+), 81 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/pom.xml
----------------------------------------------------------------------
diff --git a/mdn/pom.xml b/mdn/pom.xml
index 0a7be2b..3094c9d 100644
--- a/mdn/pom.xml
+++ b/mdn/pom.xml
@@ -29,7 +29,6 @@
 
     <artifactId>james-mdn</artifactId>
 
-
     <name>Apache James :: MDN</name>
     <description>Provides parser and representations for MDN messages</description>
 
@@ -44,10 +43,15 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>nl.jqno.equalsverifier</groupId>
+            <artifactId>equalsverifier</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.assertj</groupId>
             <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
         </dependency>
     </dependencies>
 
-
 </project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/main/java/org/apache/james/mdn/MDNReport.java
----------------------------------------------------------------------
diff --git a/mdn/src/main/java/org/apache/james/mdn/MDNReport.java b/mdn/src/main/java/org/apache/james/mdn/MDNReport.java
index 72eade2..06b66b2 100644
--- a/mdn/src/main/java/org/apache/james/mdn/MDNReport.java
+++ b/mdn/src/main/java/org/apache/james/mdn/MDNReport.java
@@ -20,6 +20,7 @@
 package org.apache.james.mdn;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
@@ -32,6 +33,7 @@ import org.apache.james.mdn.fields.OriginalMessageId;
 import org.apache.james.mdn.fields.OriginalRecipient;
 import org.apache.james.mdn.fields.ReportingUserAgent;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 
@@ -138,7 +140,8 @@ public class MDNReport {
     private final ImmutableList<Error> errorFields;
     private final ImmutableList<ExtensionField> extensionFields;
 
-    private MDNReport(Optional<ReportingUserAgent> reportingUserAgentField, Optional<Gateway> gatewayField, Optional<OriginalRecipient> originalRecipientField,
+    @VisibleForTesting
+    MDNReport(Optional<ReportingUserAgent> reportingUserAgentField, Optional<Gateway> gatewayField, Optional<OriginalRecipient> originalRecipientField,
                       FinalRecipient finalRecipientField, Optional<OriginalMessageId> originalMessageIdField, Disposition dispositionField,
                       ImmutableList<Error> errorFields, ImmutableList<ExtensionField> extensionFields) {
         this.reportingUserAgentField = reportingUserAgentField;
@@ -207,4 +210,32 @@ public class MDNReport {
             .map(ExtensionField::formattedValue)
             .collect(Collectors.joining(EXTENSION_DELIMITER)) + LINE_END;
     }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof MDNReport) {
+            MDNReport that = (MDNReport) o;
+
+            return Objects.equals(this.reportingUserAgentField, that.reportingUserAgentField)
+                && Objects.equals(this.dispositionField, that.dispositionField)
+                && Objects.equals(this.errorFields, that.errorFields)
+                && Objects.equals(this.finalRecipientField, that.finalRecipientField)
+                && Objects.equals(this.gatewayField, that.gatewayField)
+                && Objects.equals(this.originalMessageIdField, that.originalMessageIdField)
+                && Objects.equals(this.extensionFields, that.extensionFields)
+                && Objects.equals(this.originalRecipientField, that.originalRecipientField);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(reportingUserAgentField, gatewayField, originalMessageIdField, originalRecipientField,
+            dispositionField, errorFields, extensionFields, finalRecipientField);
+    }
+
+    @Override
+    public String toString() {
+        return formattedValue();
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/main/java/org/apache/james/mdn/fields/Disposition.java
----------------------------------------------------------------------
diff --git a/mdn/src/main/java/org/apache/james/mdn/fields/Disposition.java b/mdn/src/main/java/org/apache/james/mdn/fields/Disposition.java
index 157b4b7..815dcdc 100644
--- a/mdn/src/main/java/org/apache/james/mdn/fields/Disposition.java
+++ b/mdn/src/main/java/org/apache/james/mdn/fields/Disposition.java
@@ -20,6 +20,7 @@
 package org.apache.james.mdn.fields;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
@@ -28,6 +29,7 @@ import org.apache.james.mdn.modifier.DispositionModifier;
 import org.apache.james.mdn.sending.mode.DispositionSendingMode;
 import org.apache.james.mdn.type.DispositionType;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 
@@ -86,7 +88,8 @@ public class Disposition implements Field {
     private final DispositionType type;
     private final List<DispositionModifier> modifiers;
 
-    private Disposition(DispositionActionMode actionMode, DispositionSendingMode sendingMode, DispositionType type, List<DispositionModifier> modifiers) {
+    @VisibleForTesting
+    Disposition(DispositionActionMode actionMode, DispositionSendingMode sendingMode, DispositionType type, List<DispositionModifier> modifiers) {
         this.actionMode = actionMode;
         this.sendingMode = sendingMode;
         this.type = type;
@@ -124,4 +127,27 @@ public class Disposition implements Field {
             .map(DispositionModifier::getValue)
             .collect(Collectors.joining(","));
     }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof Disposition) {
+            Disposition that = (Disposition) o;
+
+            return Objects.equals(actionMode, that.actionMode)
+                && Objects.equals(sendingMode, that.sendingMode)
+                && Objects.equals(type, that.type)
+                && Objects.equals(modifiers, that.modifiers);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(actionMode, sendingMode, type, modifiers);
+    }
+
+    @Override
+    public String toString() {
+        return formattedValue();
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/main/java/org/apache/james/mdn/fields/Error.java
----------------------------------------------------------------------
diff --git a/mdn/src/main/java/org/apache/james/mdn/fields/Error.java b/mdn/src/main/java/org/apache/james/mdn/fields/Error.java
index 64dface..7001364 100644
--- a/mdn/src/main/java/org/apache/james/mdn/fields/Error.java
+++ b/mdn/src/main/java/org/apache/james/mdn/fields/Error.java
@@ -19,6 +19,10 @@
 
 package org.apache.james.mdn.fields;
 
+import java.util.Objects;
+
+import com.google.common.base.Preconditions;
+
 /**
  * Implements the optional MDN Error field defined in RFC-8098
  *
@@ -30,6 +34,7 @@ public class Error implements Field {
     private final Text text;
 
     public Error(Text text) {
+        Preconditions.checkNotNull(text);
         this.text = text;
     }
 
@@ -41,4 +46,24 @@ public class Error implements Field {
     public String formattedValue() {
         return FIELD_NAME + ": " + text.formatted();
     }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof Error) {
+            Error error = (Error) o;
+
+            return Objects.equals(text, error.text);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(text);
+    }
+
+    @Override
+    public String toString() {
+        return formattedValue();
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/main/java/org/apache/james/mdn/fields/ExtensionField.java
----------------------------------------------------------------------
diff --git a/mdn/src/main/java/org/apache/james/mdn/fields/ExtensionField.java b/mdn/src/main/java/org/apache/james/mdn/fields/ExtensionField.java
index 9b6e6bc..893d1e6 100644
--- a/mdn/src/main/java/org/apache/james/mdn/fields/ExtensionField.java
+++ b/mdn/src/main/java/org/apache/james/mdn/fields/ExtensionField.java
@@ -19,6 +19,10 @@
 
 package org.apache.james.mdn.fields;
 
+import java.util.Objects;
+
+import com.google.common.base.Preconditions;
+
 /**
  * Implements extension fields allowed by RFC-8098
  *
@@ -29,6 +33,10 @@ public class ExtensionField implements Field {
     private final String rawValue;
 
     public ExtensionField(String fieldName, String rawValue) {
+        Preconditions.checkNotNull(fieldName);
+        Preconditions.checkNotNull(rawValue);
+        Preconditions.checkArgument(!fieldName.contains("\n"), "Field name can not be multiline");
+
         this.fieldName = fieldName;
         this.rawValue = rawValue;
     }
@@ -37,4 +45,25 @@ public class ExtensionField implements Field {
     public String formattedValue() {
         return fieldName + ": " +rawValue;
     }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof ExtensionField) {
+            ExtensionField that = (ExtensionField) o;
+
+            return Objects.equals(fieldName, that.fieldName)
+                && Objects.equals(rawValue, that.rawValue);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(fieldName, rawValue);
+    }
+
+    @Override
+    public String toString() {
+        return formattedValue();
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/main/java/org/apache/james/mdn/fields/FinalRecipient.java
----------------------------------------------------------------------
diff --git a/mdn/src/main/java/org/apache/james/mdn/fields/FinalRecipient.java b/mdn/src/main/java/org/apache/james/mdn/fields/FinalRecipient.java
index b613747..8f87170 100644
--- a/mdn/src/main/java/org/apache/james/mdn/fields/FinalRecipient.java
+++ b/mdn/src/main/java/org/apache/james/mdn/fields/FinalRecipient.java
@@ -19,6 +19,8 @@
 
 package org.apache.james.mdn.fields;
 
+import java.util.Objects;
+
 import org.apache.james.mdn.Constants;
 
 import com.google.common.base.Preconditions;
@@ -37,6 +39,8 @@ public class FinalRecipient implements Field {
     public FinalRecipient(String addressType, Text finalRecipient) {
         Preconditions.checkNotNull(finalRecipient);
         Preconditions.checkNotNull(addressType);
+        Preconditions.checkArgument(!addressType.contains("\n"), "Address type can not be multiline");
+
         this.finalRecipient = finalRecipient;
         this.addressType = addressType;
     }
@@ -53,4 +57,25 @@ public class FinalRecipient implements Field {
     public String formattedValue() {
         return FIELD_NAME + ": " + addressType + "; " + finalRecipient.formatted();
     }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof FinalRecipient) {
+            FinalRecipient that = (FinalRecipient) o;
+
+            return Objects.equals(finalRecipient, that.finalRecipient)
+                && Objects.equals(addressType, that.addressType);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(finalRecipient, addressType);
+    }
+
+    @Override
+    public String toString() {
+        return formattedValue();
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/main/java/org/apache/james/mdn/fields/Gateway.java
----------------------------------------------------------------------
diff --git a/mdn/src/main/java/org/apache/james/mdn/fields/Gateway.java b/mdn/src/main/java/org/apache/james/mdn/fields/Gateway.java
index 493aa6b..f11bc7a 100644
--- a/mdn/src/main/java/org/apache/james/mdn/fields/Gateway.java
+++ b/mdn/src/main/java/org/apache/james/mdn/fields/Gateway.java
@@ -19,17 +19,25 @@
 
 package org.apache.james.mdn.fields;
 
+import java.util.Objects;
+
+import com.google.common.base.Preconditions;
+
 /**
  * MDN-Gateway field as specified in https://tools.ietf.org/html/rfc8098#section-3.2.2
  */
 public class Gateway implements Field {
-    private static final String DNS = "dns";
+    public static final String DNS = "dns";
     public static final String FIELD_NAME = "MDN-Gateway";
 
     private final String nameType;
     private final Text name;
 
     public Gateway(String nameType, Text name) {
+        Preconditions.checkNotNull(nameType);
+        Preconditions.checkNotNull(name);
+        Preconditions.checkArgument(!nameType.contains("\n"));
+
         this.nameType = nameType;
         this.name = name;
     }
@@ -50,4 +58,25 @@ public class Gateway implements Field {
     public Text getName() {
         return name;
     }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof Gateway) {
+            Gateway gateway = (Gateway) o;
+
+            return Objects.equals(nameType, gateway.nameType)
+                && Objects.equals(name, gateway.name);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(nameType, name);
+    }
+
+    @Override
+    public String toString() {
+        return formattedValue();
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/main/java/org/apache/james/mdn/fields/OriginalMessageId.java
----------------------------------------------------------------------
diff --git a/mdn/src/main/java/org/apache/james/mdn/fields/OriginalMessageId.java b/mdn/src/main/java/org/apache/james/mdn/fields/OriginalMessageId.java
index 96a1245..8d0c6f1 100644
--- a/mdn/src/main/java/org/apache/james/mdn/fields/OriginalMessageId.java
+++ b/mdn/src/main/java/org/apache/james/mdn/fields/OriginalMessageId.java
@@ -19,6 +19,10 @@
 
 package org.apache.james.mdn.fields;
 
+import java.util.Objects;
+
+import com.google.common.base.Preconditions;
+
 /**
  * Optional Original-Messageāˆ’Id as defined in RFC-8098
  *
@@ -29,6 +33,9 @@ public class OriginalMessageId implements Field {
     private final String originalMessageId;
 
     public OriginalMessageId(String originalMessageId) {
+        Preconditions.checkNotNull(originalMessageId);
+        Preconditions.checkArgument(!originalMessageId.contains("\n"));
+
         this.originalMessageId = originalMessageId;
     }
 
@@ -40,4 +47,24 @@ public class OriginalMessageId implements Field {
     public String formattedValue() {
         return FIELD_NAME + ": " + originalMessageId;
     }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof OriginalMessageId) {
+            OriginalMessageId that = (OriginalMessageId) o;
+
+            return Objects.equals(originalMessageId, that.originalMessageId);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(originalMessageId);
+    }
+
+    @Override
+    public String toString() {
+        return formattedValue();
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/main/java/org/apache/james/mdn/fields/OriginalRecipient.java
----------------------------------------------------------------------
diff --git a/mdn/src/main/java/org/apache/james/mdn/fields/OriginalRecipient.java b/mdn/src/main/java/org/apache/james/mdn/fields/OriginalRecipient.java
index ccb69c2..d1c7d33 100644
--- a/mdn/src/main/java/org/apache/james/mdn/fields/OriginalRecipient.java
+++ b/mdn/src/main/java/org/apache/james/mdn/fields/OriginalRecipient.java
@@ -19,6 +19,8 @@
 
 package org.apache.james.mdn.fields;
 
+import java.util.Objects;
+
 import org.apache.james.mdn.Constants;
 
 import com.google.common.base.Preconditions;
@@ -28,7 +30,7 @@ import com.google.common.base.Preconditions;
  *
  * https://tools.ietf.org/html/rfc8098#section-3.2.3
  */
-public class OriginalRecipient implements Field{
+public class OriginalRecipient implements Field {
     private static final String UNKNOWN = "unknown";
     private static final String FIELD_NAME = "Original-Recipient";
 
@@ -59,7 +61,28 @@ public class OriginalRecipient implements Field{
     }
 
     @Override
+    public final boolean equals(Object o) {
+        if (o instanceof OriginalRecipient) {
+            OriginalRecipient that = (OriginalRecipient) o;
+
+            return Objects.equals(this.originalRecipient, that.originalRecipient)
+                && Objects.equals(this.addressType, that.addressType);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(originalRecipient, addressType);
+    }
+
+    @Override
     public String formattedValue() {
         return FIELD_NAME + ": " + addressType + "; " + originalRecipient.formatted();
     }
+
+    @Override
+    public String toString() {
+        return formattedValue();
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/main/java/org/apache/james/mdn/fields/ReportingUserAgent.java
----------------------------------------------------------------------
diff --git a/mdn/src/main/java/org/apache/james/mdn/fields/ReportingUserAgent.java b/mdn/src/main/java/org/apache/james/mdn/fields/ReportingUserAgent.java
index 663a27a..0772049 100644
--- a/mdn/src/main/java/org/apache/james/mdn/fields/ReportingUserAgent.java
+++ b/mdn/src/main/java/org/apache/james/mdn/fields/ReportingUserAgent.java
@@ -19,8 +19,10 @@
 
 package org.apache.james.mdn.fields;
 
+import java.util.Objects;
 import java.util.Optional;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 
 /**
@@ -33,9 +35,19 @@ public class ReportingUserAgent implements Field {
     private final String userAgentName;
     private final Optional<String> userAgentProduct;
 
-    public ReportingUserAgent(String userAgentName, Optional<String> userAgentProduct) {
+    public ReportingUserAgent(String userAgentName) {
+        this(userAgentName, Optional.empty());
+    }
+
+    public ReportingUserAgent(String userAgentName, String userAgentProduct) {
+        this(userAgentName, Optional.of(userAgentProduct));
+    }
+
+    @VisibleForTesting
+    ReportingUserAgent(String userAgentName, Optional<String> userAgentProduct) {
         Preconditions.checkNotNull(userAgentName);
         Preconditions.checkNotNull(userAgentProduct);
+
         this.userAgentName = userAgentName;
         this.userAgentProduct = userAgentProduct;
     }
@@ -53,4 +65,25 @@ public class ReportingUserAgent implements Field {
         return FIELD_NAME + ": " + userAgentName + "; "
             + userAgentProduct.orElse("");
     }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof ReportingUserAgent) {
+            ReportingUserAgent that = (ReportingUserAgent) o;
+
+            return Objects.equals(this.userAgentName, that.userAgentName)
+                && Objects.equals(this.userAgentProduct, that.userAgentProduct);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(userAgentName, userAgentProduct);
+    }
+
+    @Override
+    public String toString() {
+        return formattedValue();
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/main/java/org/apache/james/mdn/fields/Text.java
----------------------------------------------------------------------
diff --git a/mdn/src/main/java/org/apache/james/mdn/fields/Text.java b/mdn/src/main/java/org/apache/james/mdn/fields/Text.java
index 7473e08..d3592cf 100644
--- a/mdn/src/main/java/org/apache/james/mdn/fields/Text.java
+++ b/mdn/src/main/java/org/apache/james/mdn/fields/Text.java
@@ -20,11 +20,14 @@
 package org.apache.james.mdn.fields;
 
 import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
 import com.google.common.base.Splitter;
 
 public class Text {
 
     public static Text fromRawText(String rawText) {
+        Preconditions.checkNotNull(rawText);
         return new Text(replaceLineBreaksByContinuation(rawText));
     }
 
@@ -44,4 +47,19 @@ public class Text {
     public String formatted() {
         return content;
     }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof Text) {
+            Text that = (Text) o;
+
+            return Objects.equal(this.content, that.content);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hashCode(content);
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/main/java/org/apache/james/mdn/modifier/DispositionModifier.java
----------------------------------------------------------------------
diff --git a/mdn/src/main/java/org/apache/james/mdn/modifier/DispositionModifier.java b/mdn/src/main/java/org/apache/james/mdn/modifier/DispositionModifier.java
index f4a2b70..f7e9171 100644
--- a/mdn/src/main/java/org/apache/james/mdn/modifier/DispositionModifier.java
+++ b/mdn/src/main/java/org/apache/james/mdn/modifier/DispositionModifier.java
@@ -19,6 +19,10 @@
 
 package org.apache.james.mdn.modifier;
 
+import java.util.Objects;
+
+import com.google.common.base.Preconditions;
+
 /**
  * Interface <code>DispositionModifier</code> marks a type encapsulating
  * disposition modifier information as defined by RFC 8098.
@@ -26,20 +30,42 @@ package org.apache.james.mdn.modifier;
  * https://tools.ietf.org/html/rfc8098#section-3.2.6.3
  */
 public class DispositionModifier {
-    public static DispositionModifier Error = new DispositionModifier("error");
-    public static DispositionModifier Expired = new DispositionModifier("expired");
-    public static DispositionModifier Failed = new DispositionModifier("failed");
-    public static DispositionModifier MailboxTerminated = new DispositionModifier("mailbox-terminated");
-    public static DispositionModifier Superseded = new DispositionModifier("superseded");
-    public static DispositionModifier Warning = new DispositionModifier("warning");
+    public static final DispositionModifier Error = new DispositionModifier("error");
+    public static final DispositionModifier Expired = new DispositionModifier("expired");
+    public static final DispositionModifier Failed = new DispositionModifier("failed");
+    public static final DispositionModifier MailboxTerminated = new DispositionModifier("mailbox-terminated");
+    public static final DispositionModifier Superseded = new DispositionModifier("superseded");
+    public static final DispositionModifier Warning = new DispositionModifier("warning");
 
     private final String value;
 
     public DispositionModifier(String value) {
+        Preconditions.checkNotNull(value);
+        Preconditions.checkArgument(!value.contains("\n"), "Multiline Disposition modifier are forbiden");
         this.value = value;
     }
 
     public String getValue() {
         return value;
     }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof DispositionModifier) {
+            DispositionModifier that = (DispositionModifier) o;
+
+            return Objects.equals(this.value, that.value);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(value);
+    }
+
+    @Override
+    public String toString() {
+        return getValue();
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/test/java/org/apache/james/mdn/MDNFactoryTest.java
----------------------------------------------------------------------
diff --git a/mdn/src/test/java/org/apache/james/mdn/MDNFactoryTest.java b/mdn/src/test/java/org/apache/james/mdn/MDNFactoryTest.java
index c9235a8..eaad55c 100644
--- a/mdn/src/test/java/org/apache/james/mdn/MDNFactoryTest.java
+++ b/mdn/src/test/java/org/apache/james/mdn/MDNFactoryTest.java
@@ -21,8 +21,6 @@ package org.apache.james.mdn;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
-import java.util.Optional;
-
 import org.apache.james.mdn.action.mode.DispositionActionMode;
 import org.apache.james.mdn.fields.Disposition;
 import org.apache.james.mdn.fields.Error;
@@ -36,15 +34,10 @@ import org.apache.james.mdn.fields.Text;
 import org.apache.james.mdn.modifier.DispositionModifier;
 import org.apache.james.mdn.sending.mode.DispositionSendingMode;
 import org.apache.james.mdn.type.DispositionType;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.ExpectedException;
 
 public class MDNFactoryTest {
 
-    @Rule
-    public ExpectedException expectedException = ExpectedException.none();
-
     @Test
     public void generateMDNReportShouldFormatAutomaticActions() {
         Disposition disposition = Disposition.builder()
@@ -58,7 +51,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))
@@ -87,7 +80,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))
@@ -116,7 +109,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))
@@ -145,7 +138,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))
@@ -174,7 +167,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))
@@ -203,7 +196,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))
@@ -231,7 +224,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))
@@ -259,7 +252,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))
@@ -286,7 +279,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))
@@ -313,9 +306,7 @@ public class MDNFactoryTest {
             .build();
 
         String report = MDNReport.builder()
-            .reportingUserAgentField(new ReportingUserAgent(
-                "UA_name",
-                Optional.empty()))
+            .reportingUserAgentField(new ReportingUserAgent("UA_name"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))
@@ -344,7 +335,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))
             .dispositionField(disposition)
@@ -371,7 +362,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
             .dispositionField(disposition)
@@ -386,19 +377,6 @@ public class MDNFactoryTest {
     }
 
     @Test
-    public void generateMDNReportThrowOnNullDisposition() {
-        expectedException.expect(IllegalStateException.class);
-
-        MDNReport.builder()
-            .reportingUserAgentField(new ReportingUserAgent(
-                "UA_name",
-                Optional.of("UA_product")))
-            .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
-            .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
-            .build();
-    }
-
-    @Test
     public void generateMDNReportShouldFormatGateway() {
         Disposition disposition = Disposition.builder()
             .actionMode(DispositionActionMode.Automatic)
@@ -411,7 +389,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .gatewayField(new Gateway(Text.fromRawText("host.com")))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
@@ -442,7 +420,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .gatewayField(new Gateway("postal", Text.fromRawText("5 rue Charles mercier")))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
@@ -473,7 +451,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient("roomNumber", Text.fromRawText("385")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))
@@ -502,7 +480,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .gatewayField(new Gateway("postal", Text.fromRawText("8 rue Charles mercier\n 36555 Saint Coincoin\n France")))
             .finalRecipientField(new FinalRecipient("postal", Text.fromRawText("5 rue Mercier\n 36555 Saint Coincoin\n France")))
             .originalRecipientField(new OriginalRecipient("postal", Text.fromRawText("3 rue Mercier\n 36555 Saint Coincoin\n France")))
@@ -539,7 +517,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(OriginalRecipient.ofUnknown(Text.fromRawText("#$%*")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))
@@ -568,7 +546,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient("roomNumber", Text.fromRawText("781")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))
@@ -585,28 +563,6 @@ public class MDNFactoryTest {
     }
 
     @Test
-    public void generateMDNReportShouldThrowWhenMissingFinalField() {
-        Disposition disposition = Disposition.builder()
-            .actionMode(DispositionActionMode.Automatic)
-            .sendingMode(DispositionSendingMode.Automatic)
-            .type(DispositionType.Processed)
-            .addModifier(DispositionModifier.Error)
-            .addModifier(DispositionModifier.Failed)
-            .build();
-
-        expectedException.expect(IllegalStateException.class);
-
-        MDNReport.builder()
-            .reportingUserAgentField(new ReportingUserAgent(
-                "UA_name",
-                Optional.of("UA_product")))
-            .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
-            .originalMessageIdField(new OriginalMessageId("original_message_id"))
-            .dispositionField(disposition)
-            .build();
-    }
-
-    @Test
     public void generateMDNReportShouldFormatErrorField() {
         Disposition disposition = Disposition.builder()
             .actionMode(DispositionActionMode.Automatic)
@@ -619,7 +575,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))
@@ -650,7 +606,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))
@@ -684,7 +640,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))
@@ -716,7 +672,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))
@@ -748,7 +704,7 @@ public class MDNFactoryTest {
         String report = MDNReport.builder()
             .reportingUserAgentField(new ReportingUserAgent(
                 "UA_name",
-                Optional.of("UA_product")))
+                "UA_product"))
             .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
             .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
             .originalMessageIdField(new OriginalMessageId("original_message_id"))

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/test/java/org/apache/james/mdn/MDNReportTest.java
----------------------------------------------------------------------
diff --git a/mdn/src/test/java/org/apache/james/mdn/MDNReportTest.java b/mdn/src/test/java/org/apache/james/mdn/MDNReportTest.java
new file mode 100644
index 0000000..4d21946
--- /dev/null
+++ b/mdn/src/test/java/org/apache/james/mdn/MDNReportTest.java
@@ -0,0 +1,150 @@
+/****************************************************************
+ * 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.mdn;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Optional;
+
+import org.apache.james.mdn.action.mode.DispositionActionMode;
+import org.apache.james.mdn.fields.Disposition;
+import org.apache.james.mdn.fields.Error;
+import org.apache.james.mdn.fields.ExtensionField;
+import org.apache.james.mdn.fields.FinalRecipient;
+import org.apache.james.mdn.fields.Gateway;
+import org.apache.james.mdn.fields.OriginalMessageId;
+import org.apache.james.mdn.fields.OriginalRecipient;
+import org.apache.james.mdn.fields.ReportingUserAgent;
+import org.apache.james.mdn.fields.Text;
+import org.apache.james.mdn.modifier.DispositionModifier;
+import org.apache.james.mdn.sending.mode.DispositionSendingMode;
+import org.apache.james.mdn.type.DispositionType;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import com.google.common.collect.ImmutableList;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class MDNReportTest {
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void shouldMatchBeanContact() {
+        EqualsVerifier.forClass(MDNReport.class)
+            .allFieldsShouldBeUsed()
+            .verify();
+    }
+
+    @Test
+    public void generateMDNReportThrowOnNullDisposition() {
+        expectedException.expect(IllegalStateException.class);
+
+        MDNReport.builder()
+            .reportingUserAgentField(new ReportingUserAgent(
+                "UA_name",
+                "UA_product"))
+            .finalRecipientField(new FinalRecipient(Text.fromRawText("final_recipient")))
+            .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
+            .build();
+    }
+
+    @Test
+    public void generateMDNReportShouldThrowWhenMissingFinalField() {
+        Disposition disposition = Disposition.builder()
+            .actionMode(DispositionActionMode.Automatic)
+            .sendingMode(DispositionSendingMode.Automatic)
+            .type(DispositionType.Processed)
+            .addModifier(DispositionModifier.Error)
+            .addModifier(DispositionModifier.Failed)
+            .build();
+
+        expectedException.expect(IllegalStateException.class);
+
+        MDNReport.builder()
+            .reportingUserAgentField(new ReportingUserAgent(
+                "UA_name",
+                "UA_product"))
+            .originalRecipientField(new OriginalRecipient(Text.fromRawText("originalRecipient")))
+            .originalMessageIdField(new OriginalMessageId("original_message_id"))
+            .dispositionField(disposition)
+            .build();
+    }
+
+    @Test
+    public void shouldBuildWithMinimalSubset() {
+        Disposition disposition = Disposition.builder()
+            .actionMode(DispositionActionMode.Automatic)
+            .sendingMode(DispositionSendingMode.Automatic)
+            .type(DispositionType.Processed)
+            .build();
+
+        FinalRecipient finalRecipientField = new FinalRecipient(Text.fromRawText("any@domain.com"));
+        MDNReport mdnReport = MDNReport.builder()
+            .finalRecipientField(finalRecipientField)
+            .dispositionField(disposition)
+            .build();
+
+        assertThat(mdnReport)
+            .isEqualTo(new MDNReport(
+                Optional.empty(), Optional.empty(), Optional.empty(), finalRecipientField, Optional.empty(), disposition,
+                ImmutableList.of(), ImmutableList.of()));
+    }
+
+    @Test
+    public void shouldBuildWithMaximalSubset() {
+        Disposition disposition = Disposition.builder()
+            .actionMode(DispositionActionMode.Automatic)
+            .sendingMode(DispositionSendingMode.Automatic)
+            .type(DispositionType.Processed)
+            .build();
+        FinalRecipient finalRecipientField = new FinalRecipient(Text.fromRawText("any@domain.com"));
+        ExtensionField extensionField1 = new ExtensionField("name1", "value1");
+        ExtensionField extensionField2 = new ExtensionField("name2", "value2");
+        Gateway gateway = new Gateway(Text.fromRawText("address"));
+        OriginalMessageId originalMessageIdField = new OriginalMessageId("msgId");
+        OriginalRecipient originalRecipientField = new OriginalRecipient(Text.fromRawText("address"));
+        ReportingUserAgent reportingUserAgentField = new ReportingUserAgent("name");
+        Error errorField1 = new Error(Text.fromRawText("error 1"));
+        Error errorField2 = new Error(Text.fromRawText("error 2"));
+
+        MDNReport mdnReport = MDNReport.builder()
+            .withExtensionField(extensionField1)
+            .withExtensionField(extensionField2)
+            .finalRecipientField(finalRecipientField)
+            .dispositionField(disposition)
+            .gatewayField(gateway)
+            .originalMessageIdField(originalMessageIdField)
+            .originalRecipientField(originalRecipientField)
+            .reportingUserAgentField(reportingUserAgentField)
+            .addErrorField(errorField1)
+            .addErrorField(errorField2)
+            .build();
+
+        assertThat(mdnReport)
+            .isEqualTo(new MDNReport(
+                Optional.of(reportingUserAgentField), Optional.of(gateway), Optional.of(originalRecipientField),
+                finalRecipientField, Optional.of(originalMessageIdField), disposition,
+                ImmutableList.of(errorField1, errorField2), ImmutableList.of(extensionField1, extensionField2)));
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/test/java/org/apache/james/mdn/action/mode/DispositionActionModeTest.java
----------------------------------------------------------------------
diff --git a/mdn/src/test/java/org/apache/james/mdn/action/mode/DispositionActionModeTest.java b/mdn/src/test/java/org/apache/james/mdn/action/mode/DispositionActionModeTest.java
new file mode 100644
index 0000000..219dff2
--- /dev/null
+++ b/mdn/src/test/java/org/apache/james/mdn/action/mode/DispositionActionModeTest.java
@@ -0,0 +1,49 @@
+/****************************************************************
+ * 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.mdn.action.mode;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Test;
+
+public class DispositionActionModeTest {
+    @Test
+    public void fromStringShouldReturnEmptyWhenUnknown() {
+        assertThat(DispositionActionMode.fromString("unknown"))
+            .isEmpty();
+    }
+
+    @Test
+    public void fromStringShouldRetrieveAutomatic() {
+        assertThat(DispositionActionMode.fromString(DispositionActionMode.Automatic.getValue()))
+            .contains(DispositionActionMode.Automatic);
+    }
+
+    @Test
+    public void fromStringShouldRetrieveManual() {
+        assertThat(DispositionActionMode.fromString(DispositionActionMode.Manual.getValue()))
+            .contains(DispositionActionMode.Manual);
+    }
+    @Test
+    public void fromStringShouldNotBeCaseSensitive() {
+        assertThat(DispositionActionMode.fromString("autoMatic-action"))
+            .contains(DispositionActionMode.Automatic);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/test/java/org/apache/james/mdn/fields/DispositionTest.java
----------------------------------------------------------------------
diff --git a/mdn/src/test/java/org/apache/james/mdn/fields/DispositionTest.java b/mdn/src/test/java/org/apache/james/mdn/fields/DispositionTest.java
new file mode 100644
index 0000000..817d731
--- /dev/null
+++ b/mdn/src/test/java/org/apache/james/mdn/fields/DispositionTest.java
@@ -0,0 +1,177 @@
+/****************************************************************
+ * 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.mdn.fields;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.james.mdn.action.mode.DispositionActionMode;
+import org.apache.james.mdn.modifier.DispositionModifier;
+import org.apache.james.mdn.sending.mode.DispositionSendingMode;
+import org.apache.james.mdn.type.DispositionType;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import com.google.common.collect.ImmutableList;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class DispositionTest {
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void shouldMatchBeanContract() throws Exception {
+        EqualsVerifier.forClass(Disposition.class)
+            .allFieldsShouldBeUsed()
+            .verify();
+    }
+
+    @Test
+    public void shouldBuildMinimalSubSet() {
+        Disposition disposition = Disposition.builder()
+            .actionMode(DispositionActionMode.Automatic)
+            .sendingMode(DispositionSendingMode.Automatic)
+            .type(DispositionType.Processed)
+            .build();
+
+        ImmutableList<DispositionModifier> modifiers = ImmutableList.of();
+        assertThat(disposition)
+            .isEqualTo(new Disposition(
+                DispositionActionMode.Automatic,
+                DispositionSendingMode.Automatic,
+                DispositionType.Processed,
+                modifiers));
+    }
+
+    @Test
+    public void buildShouldThrowOnMissingActionMode() {
+        expectedException.expect(IllegalStateException.class);
+
+        Disposition.builder()
+            .sendingMode(DispositionSendingMode.Automatic)
+            .type(DispositionType.Processed)
+            .build();
+    }
+
+    @Test
+    public void buildShouldThrowOnMissingSendingMode() {
+        expectedException.expect(IllegalStateException.class);
+
+        Disposition.builder()
+            .actionMode(DispositionActionMode.Automatic)
+            .type(DispositionType.Processed)
+            .build();
+    }
+
+    @Test
+    public void buildShouldThrowOnMissingType() {
+        expectedException.expect(IllegalStateException.class);
+
+        Disposition.builder()
+            .actionMode(DispositionActionMode.Automatic)
+            .sendingMode(DispositionSendingMode.Automatic)
+            .build();
+    }
+
+    @Test
+    public void shouldBuildWithAllOptions() {
+        Disposition disposition = Disposition.builder()
+            .actionMode(DispositionActionMode.Automatic)
+            .sendingMode(DispositionSendingMode.Automatic)
+            .type(DispositionType.Processed)
+            .addModifiers(DispositionModifier.Expired, DispositionModifier.Warning)
+            .build();
+
+        ImmutableList<DispositionModifier> modifiers = ImmutableList.of(DispositionModifier.Expired, DispositionModifier.Warning);
+        assertThat(disposition)
+            .isEqualTo(new Disposition(
+                DispositionActionMode.Automatic,
+                DispositionSendingMode.Automatic,
+                DispositionType.Processed,
+                modifiers));
+    }
+
+    @Test
+    public void formattedValueShouldDisplayAllOptions() {
+        Disposition disposition = Disposition.builder()
+            .actionMode(DispositionActionMode.Automatic)
+            .sendingMode(DispositionSendingMode.Automatic)
+            .type(DispositionType.Processed)
+            .addModifiers(DispositionModifier.Expired, DispositionModifier.Warning)
+            .build();
+
+        assertThat(disposition.formattedValue())
+            .isEqualTo("Disposition: automatic-action/MDN-sent-automatically;processed/expired,warning");
+    }
+
+    @Test
+    public void formattedValueShouldDisplaySingleModifier() {
+        Disposition disposition = Disposition.builder()
+            .actionMode(DispositionActionMode.Automatic)
+            .sendingMode(DispositionSendingMode.Automatic)
+            .type(DispositionType.Processed)
+            .addModifiers(DispositionModifier.Expired)
+            .build();
+
+        assertThat(disposition.formattedValue())
+            .isEqualTo("Disposition: automatic-action/MDN-sent-automatically;processed/expired");
+    }
+
+
+    @Test
+    public void formattedValueShouldDisplayNoModifier() {
+        Disposition disposition = Disposition.builder()
+            .actionMode(DispositionActionMode.Automatic)
+            .sendingMode(DispositionSendingMode.Automatic)
+            .type(DispositionType.Processed)
+            .build();
+
+        assertThat(disposition.formattedValue())
+            .isEqualTo("Disposition: automatic-action/MDN-sent-automatically;processed");
+    }
+
+    @Test
+    public void formattedValueShouldDisplayManualActionMode() {
+        Disposition disposition = Disposition.builder()
+            .actionMode(DispositionActionMode.Manual)
+            .sendingMode(DispositionSendingMode.Automatic)
+            .type(DispositionType.Processed)
+            .addModifiers(DispositionModifier.Expired)
+            .build();
+
+        assertThat(disposition.formattedValue())
+            .isEqualTo("Disposition: manual-action/MDN-sent-automatically;processed/expired");
+    }
+
+    @Test
+    public void formattedValueShouldDisplayManualSendingMode() {
+        Disposition disposition = Disposition.builder()
+            .actionMode(DispositionActionMode.Automatic)
+            .sendingMode(DispositionSendingMode.Manual)
+            .type(DispositionType.Processed)
+            .addModifiers(DispositionModifier.Expired)
+            .build();
+
+        assertThat(disposition.formattedValue())
+            .isEqualTo("Disposition: automatic-action/MDN-sent-manually;processed/expired");
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/test/java/org/apache/james/mdn/fields/ErrorTest.java
----------------------------------------------------------------------
diff --git a/mdn/src/test/java/org/apache/james/mdn/fields/ErrorTest.java b/mdn/src/test/java/org/apache/james/mdn/fields/ErrorTest.java
new file mode 100644
index 0000000..294b90b
--- /dev/null
+++ b/mdn/src/test/java/org/apache/james/mdn/fields/ErrorTest.java
@@ -0,0 +1,62 @@
+/****************************************************************
+ * 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.mdn.fields;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class ErrorTest {
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void shouldMatchBeanContract() throws Exception {
+        EqualsVerifier.forClass(Error.class)
+            .allFieldsShouldBeUsed()
+            .verify();
+    }
+
+    @Test
+    public void shouldThrowOnNullText() {
+        expectedException.expect(NullPointerException.class);
+
+        new Error(null);
+    }
+
+    @Test
+    public void formattedValueShouldDisplayMessage() {
+        assertThat(new Error(Text.fromRawText("Message"))
+            .formattedValue())
+            .isEqualTo("Error: Message");
+    }
+
+    @Test
+    public void formattedValueShouldDisplayMultiLineMessage() {
+        assertThat(new Error(Text.fromRawText("Multi\nline\nMessage"))
+            .formattedValue())
+            .isEqualTo("Error: Multi\r\n line\r\n Message");
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/test/java/org/apache/james/mdn/fields/ExtensionFieldTest.java
----------------------------------------------------------------------
diff --git a/mdn/src/test/java/org/apache/james/mdn/fields/ExtensionFieldTest.java b/mdn/src/test/java/org/apache/james/mdn/fields/ExtensionFieldTest.java
new file mode 100644
index 0000000..7c9e877
--- /dev/null
+++ b/mdn/src/test/java/org/apache/james/mdn/fields/ExtensionFieldTest.java
@@ -0,0 +1,71 @@
+/****************************************************************
+ * 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.mdn.fields;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class ExtensionFieldTest {
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void shouldMatchBeanContract() throws Exception {
+        EqualsVerifier.forClass(ExtensionField.class)
+            .allFieldsShouldBeUsed()
+            .verify();
+    }
+
+    @Test
+    public void shouldThrowOnNullFieldName() {
+        expectedException.expect(NullPointerException.class);
+
+        String fieldName = null;
+        new ExtensionField(fieldName, "rawValue");
+    }
+
+    @Test
+    public void shouldThrowOnNullRawValue() {
+        expectedException.expect(NullPointerException.class);
+
+        String rawValue = null;
+        new ExtensionField("name", rawValue);
+    }
+
+    @Test
+    public void shouldThrowOnMultilineName() {
+        expectedException.expect(IllegalArgumentException.class);
+
+        new ExtensionField("name\nmultiline", "rawValue");
+    }
+
+    @Test
+    public void formattedValueShouldDisplayNameAndRawValue() {
+        assertThat(new ExtensionField("name", "rawValue")
+            .formattedValue())
+            .isEqualTo("name: rawValue");
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/test/java/org/apache/james/mdn/fields/FinalRecipientTest.java
----------------------------------------------------------------------
diff --git a/mdn/src/test/java/org/apache/james/mdn/fields/FinalRecipientTest.java b/mdn/src/test/java/org/apache/james/mdn/fields/FinalRecipientTest.java
new file mode 100644
index 0000000..c424a0c
--- /dev/null
+++ b/mdn/src/test/java/org/apache/james/mdn/fields/FinalRecipientTest.java
@@ -0,0 +1,101 @@
+/****************************************************************
+ * 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.mdn.fields;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.james.mdn.Constants;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class FinalRecipientTest {
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void shouldMatchBeanContract() throws Exception {
+        EqualsVerifier.forClass(FinalRecipient.class)
+            .allFieldsShouldBeUsed()
+            .verify();
+    }
+
+    @Test
+    public void shouldThrowOnNullAddress() {
+        expectedException.expect(NullPointerException.class);
+
+        new FinalRecipient(null);
+    }
+
+    @Test
+    public void shouldThrowOnNullAddressWithType() {
+        expectedException.expect(NullPointerException.class);
+
+        new FinalRecipient("customType", null);
+    }
+
+    @Test
+    public void shouldThrowOnNullType() {
+        expectedException.expect(NullPointerException.class);
+
+        String addressType = null;
+        new FinalRecipient(addressType, Text.fromRawText("address"));
+    }
+
+    @Test
+    public void shouldThrowOnMultilineType() {
+        expectedException.expect(IllegalArgumentException.class);
+
+        String addressType = "a\nb";
+        new FinalRecipient(addressType, Text.fromRawText("address"));
+    }
+
+    @Test
+    public void typeShouldDefaultToRfc822() {
+        Text address = Text.fromRawText("address");
+        assertThat(new FinalRecipient(address))
+            .isEqualTo(new FinalRecipient(Constants.RFC_822, address));
+    }
+
+    @Test
+    public void formattedValueShouldDisplayAddress() {
+        assertThat(new FinalRecipient(Text.fromRawText("Plop"))
+            .formattedValue())
+            .isEqualTo("Final-Recipient: rfc822; Plop");
+    }
+
+    @Test
+    public void formattedValueShouldDisplayCustomType() {
+        assertThat(new FinalRecipient("postal", Text.fromRawText("Plop"))
+            .formattedValue())
+            .isEqualTo("Final-Recipient: postal; Plop");
+    }
+
+    @Test
+    public void formattedValueShouldDisplayMultilineAddress() {
+        assertThat(new FinalRecipient(Text.fromRawText("Plop\nGlark"))
+            .formattedValue())
+            .isEqualTo("Final-Recipient: rfc822; Plop\r\n" +
+                " Glark");
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/test/java/org/apache/james/mdn/fields/GatewayTest.java
----------------------------------------------------------------------
diff --git a/mdn/src/test/java/org/apache/james/mdn/fields/GatewayTest.java b/mdn/src/test/java/org/apache/james/mdn/fields/GatewayTest.java
new file mode 100644
index 0000000..b5041fd
--- /dev/null
+++ b/mdn/src/test/java/org/apache/james/mdn/fields/GatewayTest.java
@@ -0,0 +1,102 @@
+/****************************************************************
+ * 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.mdn.fields;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class GatewayTest {
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void shouldMatchBeanContract() throws Exception {
+        EqualsVerifier.forClass(Gateway.class)
+            .allFieldsShouldBeUsed()
+            .verify();
+    }
+
+    @Test
+    public void shouldThrowOnNullName() {
+        expectedException.expect(NullPointerException.class);
+
+        Text name = null;
+        new Gateway(name);
+    }
+
+    @Test
+    public void shouldThrowOnNullNameWhenType() {
+        expectedException.expect(NullPointerException.class);
+
+        Text name = null;
+        new Gateway("type", name);
+    }
+
+    @Test
+    public void shouldThrowOnNullType() {
+        expectedException.expect(NullPointerException.class);
+
+        String nameType = null;
+        new Gateway(nameType, Text.fromRawText("name"));
+    }
+
+    @Test
+    public void shouldThrowOnMultilineType() {
+        expectedException.expect(IllegalArgumentException.class);
+
+        String nameType = "name\ntype";
+        new Gateway(nameType, Text.fromRawText("name"));
+    }
+
+    @Test
+    public void addressTypeSHouldDefaultToDNS() {
+        Text address = Text.fromRawText("address");
+        assertThat(new Gateway(address))
+            .isEqualTo(new Gateway(Gateway.DNS, address));
+    }
+
+    @Test
+    public void formattedValueShouldDisplayAddress() {
+        assertThat(new Gateway(Text.fromRawText("address"))
+            .formattedValue())
+            .isEqualTo("MDN-Gateway: dns;address");
+    }
+
+    @Test
+    public void formattedValueShouldDisplayMultilineAddress() {
+        assertThat(new Gateway(Text.fromRawText("address\nmultiline"))
+            .formattedValue())
+            .isEqualTo("MDN-Gateway: dns;address\r\n" +
+                " multiline");
+    }
+
+    @Test
+    public void formattedValueShouldDisplayCustomAddress() {
+        assertThat(new Gateway("custom", Text.fromRawText("address"))
+            .formattedValue())
+            .isEqualTo("MDN-Gateway: custom;address");
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/test/java/org/apache/james/mdn/fields/OriginalMessageIdTest.java
----------------------------------------------------------------------
diff --git a/mdn/src/test/java/org/apache/james/mdn/fields/OriginalMessageIdTest.java b/mdn/src/test/java/org/apache/james/mdn/fields/OriginalMessageIdTest.java
new file mode 100644
index 0000000..58f12c1
--- /dev/null
+++ b/mdn/src/test/java/org/apache/james/mdn/fields/OriginalMessageIdTest.java
@@ -0,0 +1,62 @@
+/****************************************************************
+ * 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.mdn.fields;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class OriginalMessageIdTest {
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void shouldMatchBeanContract() throws Exception {
+        EqualsVerifier.forClass(OriginalMessageId.class)
+            .allFieldsShouldBeUsed()
+            .verify();
+    }
+
+    @Test
+    public void shouldThrowOnNullMessageId() {
+        expectedException.expect(NullPointerException.class);
+
+        new OriginalMessageId(null);
+    }
+
+    @Test
+    public void shouldThrowOnMultiLineMessageId() {
+        expectedException.expect(IllegalArgumentException.class);
+
+        new OriginalMessageId("message\nid");
+    }
+
+    @Test
+    public void formattedValueShouldDisplayMessageId() {
+        assertThat(new OriginalMessageId("msgId")
+            .formattedValue())
+            .isEqualTo("Original-Message-ID: msgId");
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/test/java/org/apache/james/mdn/fields/OriginalRecipientTest.java
----------------------------------------------------------------------
diff --git a/mdn/src/test/java/org/apache/james/mdn/fields/OriginalRecipientTest.java b/mdn/src/test/java/org/apache/james/mdn/fields/OriginalRecipientTest.java
new file mode 100644
index 0000000..7a37e54
--- /dev/null
+++ b/mdn/src/test/java/org/apache/james/mdn/fields/OriginalRecipientTest.java
@@ -0,0 +1,95 @@
+/****************************************************************
+ * 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.mdn.fields;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.james.mdn.Constants;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class OriginalRecipientTest {
+    public static final Text ADDRESS = Text.fromRawText("address");
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void shouldMatchBeanContract() throws Exception {
+        EqualsVerifier.forClass(OriginalRecipient.class)
+            .allFieldsShouldBeUsed()
+            .verify();
+    }
+
+    @Test
+    public void shouldThrowOnNullAddress() {
+        expectedException.expect(NullPointerException.class);
+
+        Text originalRecipient = null;
+        new OriginalRecipient(originalRecipient);
+    }
+
+    @Test
+    public void shouldThrowOnNullAddressWhenCustomType() {
+        expectedException.expect(NullPointerException.class);
+
+        Text originalRecipient = null;
+        new OriginalRecipient("customType", originalRecipient);
+    }
+
+    @Test
+    public void shouldThrowOnNullAddressType() {
+        expectedException.expect(NullPointerException.class);
+
+        String addressType = null;
+        new OriginalRecipient(addressType, ADDRESS);
+    }
+
+    @Test
+    public void addressTypeShouldDefaultToRfc822() {
+        assertThat(new OriginalRecipient(ADDRESS))
+            .isEqualTo(new OriginalRecipient(Constants.RFC_822, ADDRESS));
+    }
+
+    @Test
+    public void formattedValueShouldDisplayAddress() {
+        assertThat(new OriginalRecipient(ADDRESS)
+            .formattedValue())
+            .isEqualTo("Original-Recipient: rfc822; address");
+    }
+
+    @Test
+    public void formattedValueShouldDisplayCustomType() {
+        assertThat(new OriginalRecipient("custom", ADDRESS)
+            .formattedValue())
+            .isEqualTo("Original-Recipient: custom; address");
+    }
+
+    @Test
+    public void formattedValueShouldDisplayMultilineAddress() {
+        assertThat(new OriginalRecipient(Text.fromRawText("multiline\naddress"))
+            .formattedValue())
+            .isEqualTo("Original-Recipient: rfc822; multiline\r\n" +
+                " address");
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/test/java/org/apache/james/mdn/fields/ReportingUserAgentTest.java
----------------------------------------------------------------------
diff --git a/mdn/src/test/java/org/apache/james/mdn/fields/ReportingUserAgentTest.java b/mdn/src/test/java/org/apache/james/mdn/fields/ReportingUserAgentTest.java
new file mode 100644
index 0000000..74f3a97
--- /dev/null
+++ b/mdn/src/test/java/org/apache/james/mdn/fields/ReportingUserAgentTest.java
@@ -0,0 +1,93 @@
+/****************************************************************
+ * 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.mdn.fields;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Optional;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class ReportingUserAgentTest {
+    public static final String USER_AGENT_NAME = "name";
+    public static final String USER_AGENT_PRODUCT = "product";
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void shouldMatchBeanContact() {
+        EqualsVerifier.forClass(ReportingUserAgent.class)
+            .allFieldsShouldBeUsed()
+            .verify();
+    }
+
+    @Test
+    public void productShouldBeOptional() {
+        assertThat(new ReportingUserAgent(USER_AGENT_NAME))
+            .isEqualTo(new ReportingUserAgent(USER_AGENT_NAME, Optional.empty()));
+    }
+
+    @Test
+    public void productShouldBePresentWhenSpecified() {
+        assertThat(new ReportingUserAgent(USER_AGENT_NAME, USER_AGENT_PRODUCT))
+            .isEqualTo(new ReportingUserAgent(USER_AGENT_NAME, Optional.of(USER_AGENT_PRODUCT)));
+    }
+
+    @Test
+    public void shouldThrowOnNullName() {
+        expectedException.expect(NullPointerException.class);
+
+        String userAgentName = null;
+        new ReportingUserAgent(userAgentName);
+    }
+
+    @Test
+    public void shouldThrowOnNullNameWhenSpecifyingProduct() {
+        expectedException.expect(NullPointerException.class);
+
+        String userAgentName = null;
+        new ReportingUserAgent(userAgentName, USER_AGENT_PRODUCT);
+    }
+
+    @Test
+    public void shouldThrowOnNullProduct() {
+        expectedException.expect(NullPointerException.class);
+
+        String userAgentProduct = null;
+        new ReportingUserAgent(USER_AGENT_NAME, userAgentProduct);
+    }
+
+    @Test
+    public void formattedValueShouldDisplayNameWhenProductMissing() {
+        assertThat(new ReportingUserAgent(USER_AGENT_NAME).formattedValue())
+            .isEqualTo("Reporting-UA: name; ");
+    }
+
+    @Test
+    public void formattedValueShouldDisplayProduct() {
+        assertThat(new ReportingUserAgent(USER_AGENT_NAME, USER_AGENT_PRODUCT).formattedValue())
+            .isEqualTo("Reporting-UA: name; product");
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4020b750/mdn/src/test/java/org/apache/james/mdn/fields/TextTest.java
----------------------------------------------------------------------
diff --git a/mdn/src/test/java/org/apache/james/mdn/fields/TextTest.java b/mdn/src/test/java/org/apache/james/mdn/fields/TextTest.java
new file mode 100644
index 0000000..938b6fe
--- /dev/null
+++ b/mdn/src/test/java/org/apache/james/mdn/fields/TextTest.java
@@ -0,0 +1,97 @@
+/****************************************************************
+ * 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.mdn.fields;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class TextTest {
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void shouldMatchBeanContact() {
+        EqualsVerifier.forClass(Text.class)
+            .allFieldsShouldBeUsed()
+            .verify();
+    }
+
+    @Test
+    public void fromRawTextShouldThrowOnNull() {
+        expectedException.expect(NullPointerException.class);
+
+        Text.fromRawText(null);
+    }
+
+    @Test
+    public void formattedShouldRenderEmptyStrings() {
+        Text text = Text.fromRawText("");
+
+        assertThat(text.formatted()).isEqualTo("");
+    }
+
+    @Test
+    public void formattedShouldKeepSpaces() {
+        Text text = Text.fromRawText("text with spaces");
+
+        assertThat(text.formatted()).isEqualTo("text with spaces");
+    }
+
+    @Test
+    public void formattedShouldWrapLines() {
+        Text text = Text.fromRawText("text with spaces\r\non several lines");
+
+        assertThat(text.formatted()).isEqualTo("text with spaces\r\n on several lines");
+    }
+
+    @Test
+    public void formattedShouldPreserveLineWrapping() {
+        Text text = Text.fromRawText("text with spaces\r\n on several lines");
+
+        assertThat(text.formatted()).isEqualTo("text with spaces\r\n on several lines");
+    }
+
+    @Test
+    public void formattedShouldTrimExtraSpacesAfterWrapping() {
+        Text text = Text.fromRawText("text with spaces\r\n  on several lines");
+
+        assertThat(text.formatted()).isEqualTo("text with spaces\r\n on several lines");
+    }
+
+    @Test
+    public void formattedShouldTrimExtraSpacesBeforeWrapping() {
+        Text text = Text.fromRawText("text with spaces  \r\non several lines");
+
+        assertThat(text.formatted()).isEqualTo("text with spaces\r\n on several lines");
+    }
+
+    @Test
+    public void formattedShouldPreserveFoldingSpaces() {
+        Text text = Text.fromRawText("text with folding    spaces");
+
+        assertThat(text.formatted()).isEqualTo("text with folding    spaces");
+    }
+}


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