Repository: hyperledger/quilt
Pull request #354 (2019-10-18 22:03:03)

Df/improve stream test vectors
* Enhance AsnUintCodecUL to provide a sensible default value in the case that StreamClose and ConnectionClose specify max-amount values that are too large.
* Cleanup the ErrorCode interface to allow for unsupported error-codes that don't violate the constraints of a Java enum.
* Introduce a new Stream Packet and Frame TestFixture that mirrors the [JS implementation](https://github.com/interledgerjs/ilp-protocol-stream/blob/master/test/fixtures/packets.json).
* Misc cleanup.

codecov[bot]: # [Codecov](https://codecov.io/gh/hyperledger/quilt/pull/354?src=pr&el=h1) Report
> Merging [#354](https://codecov.io/gh/hyperledger/quilt/pull/354?src=pr&el=desc) into [master](https://codecov.io/gh/hyperledger/quilt/commit/f0b6c9bd7599220a34c83d0eeca9cd9a581d0e7b?src=pr&el=desc) will **increase** coverage by `0.18%`.
> The diff coverage is `96.15%`.

[![Impacted file tree graph](https://codecov.io/gh/hyperledger/quilt/pull/354/graphs/tree.svg?width=650&token=XEfCq6zC6W&height=150&src=pr)](https://codecov.io/gh/hyperledger/quilt/pull/354?src=pr&el=tree)

```diff
@@             Coverage Diff              @@
##             master     #354      +/-   ##
============================================
+ Coverage      82.2%   82.38%   +0.18%     
- Complexity      792      796       +4     
============================================
  Files           237      238       +1     
  Lines          3635     3650      +15     
  Branches        220      221       +1     
============================================
+ Hits           2988     3007      +19     
+ Misses          553      547       -6     
- Partials         94       96       +2
```


| [Impacted Files](https://codecov.io/gh/hyperledger/quilt/pull/354?src=pr&el=tree) | Coverage Δ | Complexity Δ | |
|---|---|---|---|
| [...eam/frame/AsnStreamMoneyBlockedFrameDataCodec.java](https://codecov.io/gh/hyperledger/quilt/pull/354/diff?src=pr&el=tree#diff-Y29kZWNzLXBhcmVudC9jb2RlY3Mtc3RyZWFtL3NyYy9tYWluL2phdmEvb3JnL2ludGVybGVkZ2VyL2NvZGVjcy9zdHJlYW0vZnJhbWUvQXNuU3RyZWFtTW9uZXlCbG9ja2VkRnJhbWVEYXRhQ29kZWMuamF2YQ==) | `100% <ø> (ø)` | `3 <0> (ø)` | :arrow_down: |
| [.../stream/frame/AsnStreamMoneyMaxFrameDataCodec.java](https://codecov.io/gh/hyperledger/quilt/pull/354/diff?src=pr&el=tree#diff-Y29kZWNzLXBhcmVudC9jb2RlY3Mtc3RyZWFtL3NyYy9tYWluL2phdmEvb3JnL2ludGVybGVkZ2VyL2NvZGVjcy9zdHJlYW0vZnJhbWUvQXNuU3RyZWFtTW9uZXlNYXhGcmFtZURhdGFDb2RlYy5qYXZh) | `100% <ø> (ø)` | `3 <0> (ø)` | :arrow_down: |
| [...rg/interledger/stream/frames/StreamCloseFrame.java](https://codecov.io/gh/hyperledger/quilt/pull/354/diff?src=pr&el=tree#diff-c3RyZWFtLXBhcmVudC9zdHJlYW0tY29yZS9zcmMvbWFpbi9qYXZhL29yZy9pbnRlcmxlZGdlci9zdHJlYW0vZnJhbWVzL1N0cmVhbUNsb3NlRnJhbWUuamF2YQ==) | `100% <ø> (ø)` | `3 <0> (ø)` | :arrow_down: |
| [...java/org/interledger/stream/frames/ErrorCodes.java](https://codecov.io/gh/hyperledger/quilt/pull/354/diff?src=pr&el=tree#diff-c3RyZWFtLXBhcmVudC9zdHJlYW0tY29yZS9zcmMvbWFpbi9qYXZhL29yZy9pbnRlcmxlZGdlci9zdHJlYW0vZnJhbWVzL0Vycm9yQ29kZXMuamF2YQ==) | `100% <100%> (ø)` | `15 <15> (?)` | |
| [...stream/frame/AsnConnectionCloseFrameDataCodec.java](https://codecov.io/gh/hyperledger/quilt/pull/354/diff?src=pr&el=tree#diff-Y29kZWNzLXBhcmVudC9jb2RlY3Mtc3RyZWFtL3NyYy9tYWluL2phdmEvb3JnL2ludGVybGVkZ2VyL2NvZGVjcy9zdHJlYW0vZnJhbWUvQXNuQ29ubmVjdGlvbkNsb3NlRnJhbWVEYXRhQ29kZWMuamF2YQ==) | `100% <100%> (ø)` | `5 <1> (ø)` | :arrow_down: |
| [.../java/org/interledger/stream/frames/ErrorCode.java](https://codecov.io/gh/hyperledger/quilt/pull/354/diff?src=pr&el=tree#diff-c3RyZWFtLXBhcmVudC9zdHJlYW0tY29yZS9zcmMvbWFpbi9qYXZhL29yZy9pbnRlcmxlZGdlci9zdHJlYW0vZnJhbWVzL0Vycm9yQ29kZS5qYXZh) | `100% <100%> (ø)` | `1 <1> (-14)` | :arrow_down: |
| [...edger/stream/receiver/StatelessStreamReceiver.java](https://codecov.io/gh/hyperledger/quilt/pull/354/diff?src=pr&el=tree#diff-c3RyZWFtLXBhcmVudC9zdHJlYW0tcmVjZWl2ZXIvc3JjL21haW4vamF2YS9vcmcvaW50ZXJsZWRnZXIvc3RyZWFtL3JlY2VpdmVyL1N0YXRlbGVzc1N0cmVhbVJlY2VpdmVyLmphdmE=) | `94.73% <100%> (ø)` | `14 <0> (ø)` | :arrow_down: |
| [...ecs/stream/frame/AsnStreamCloseFrameDataCodec.java](https://codecov.io/gh/hyperledger/quilt/pull/354/diff?src=pr&el=tree#diff-Y29kZWNzLXBhcmVudC9jb2RlY3Mtc3RyZWFtL3NyYy9tYWluL2phdmEvb3JnL2ludGVybGVkZ2VyL2NvZGVjcy9zdHJlYW0vZnJhbWUvQXNuU3RyZWFtQ2xvc2VGcmFtZURhdGFDb2RlYy5qYXZh) | `100% <100%> (ø)` | `3 <0> (ø)` | :arrow_down: |
| [...nterledger/encoding/asn/codecs/AsnUintCodecUL.java](https://codecov.io/gh/hyperledger/quilt/pull/354/diff?src=pr&el=tree#diff-Y29kZWNzLXBhcmVudC9jb2RlY3MtZnJhbWV3b3JrL3NyYy9tYWluL2phdmEvb3JnL2ludGVybGVkZ2VyL2VuY29kaW5nL2Fzbi9jb2RlY3MvQXNuVWludENvZGVjVUwuamF2YQ==) | `66.66% <81.81%> (+66.66%)` | `3 <2> (+3)` | :arrow_up: |
| ... and [1 more](https://codecov.io/gh/hyperledger/quilt/pull/354/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/hyperledger/quilt/pull/354?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/hyperledger/quilt/pull/354?src=pr&el=footer). Last update [f0b6c9b...d724b29](https://codecov.io/gh/hyperledger/quilt/pull/354?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/codecs-parent/codecs-framework/pom.xml b/codecs-parent/codecs-framework/pom.xml
index 6bf298a..7236f96 100644
--- a/codecs-parent/codecs-framework/pom.xml
+++ b/codecs-parent/codecs-framework/pom.xml
@@ -22,16 +22,23 @@
     </dependency>
 
     <dependency>
-      <groupId>org.immutables</groupId>
-      <artifactId>value</artifactId>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
     </dependency>
 
     <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
+      <groupId>org.assertj</groupId>
+      <artifactId>assertj-core</artifactId>
       <scope>test</scope>
     </dependency>
 
+    <dependency>
+      <groupId>org.immutables</groupId>
+      <artifactId>value</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
     <dependency>
       <groupId>org.mockito</groupId>
       <artifactId>mockito-core</artifactId>
@@ -39,9 +46,8 @@
     </dependency>
 
     <dependency>
-      <groupId>org.assertj</groupId>
-      <artifactId>assertj-core</artifactId>
-      <scope>test</scope>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
     </dependency>
 
   </dependencies>
diff --git a/codecs-parent/codecs-framework/src/main/java/org/interledger/encoding/asn/codecs/AsnUintCodecUL.java b/codecs-parent/codecs-framework/src/main/java/org/interledger/encoding/asn/codecs/AsnUintCodecUL.java
index 5656e10..8732970 100644
--- a/codecs-parent/codecs-framework/src/main/java/org/interledger/encoding/asn/codecs/AsnUintCodecUL.java
+++ b/codecs-parent/codecs-framework/src/main/java/org/interledger/encoding/asn/codecs/AsnUintCodecUL.java
@@ -21,28 +21,63 @@ package org.interledger.encoding.asn.codecs;
  */
 
 import com.google.common.primitives.UnsignedLong;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.ByteArrayOutputStream;
 import java.math.BigInteger;
+import java.util.Base64;
+import java.util.Optional;
 
 /**
  * <p>An ASN.1 codec for a variable-size ASN.1 OER integer type, backed by {@link BigInteger}.</p>
  *
  * <p>Per the OER definitions, the integer value is encoded as a length prefix, followed by an
- * unsigned binary integer occupying a variable number of octets; the length prefix contains the
- * number of subsequent octets.</p>
+ * unsigned binary integer occupying a variable number of octets; the length prefix contains the number of subsequent
+ * octets.</p>
  *
  * @see "http://www.oss.com/asn1/resources/books-whitepapers-pubs/Overview_of_OER.pdf"
  */
 public class AsnUintCodecUL extends AsnOctetStringBasedObjectCodec<UnsignedLong> {
 
+  private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+  private final Optional<UnsignedLong> defaultValue;
+
   public AsnUintCodecUL() {
     super(AsnSizeConstraint.UNCONSTRAINED);
+    this.defaultValue = Optional.empty();
+  }
+
+  /**
+   * Required-args Constructor.
+   *
+   * @param defaultValue An {@link UnsignedLong} that can be used if the ASN.1 OER bytes cannot be decoded into an
+   *                     Unsigned Long.
+   */
+  public AsnUintCodecUL(final UnsignedLong defaultValue) {
+    super(AsnSizeConstraint.UNCONSTRAINED);
+    this.defaultValue = Optional.of(defaultValue);
   }
 
   @Override
   public UnsignedLong decode() {
-    return UnsignedLong.valueOf(new BigInteger(1, getBytes()));
+
+    // If the number is unable to be decoded or otherwise doesn't fit into an UnsignedLong, then an exception will be
+    // thrown. Optionally-present default values can be used, or if absent, an exception will be thrown.
+    try {
+      return UnsignedLong.valueOf(new BigInteger(1, getBytes()));
+    } catch (Exception e) {
+      if (defaultValue.isPresent()) {
+        logger.warn(
+            "Variable Unsigned Integer was too big for VarUInt: {}. Returning UnsignedLong.Max",
+            Base64.getEncoder().encodeToString(getBytes())
+        );
+        return defaultValue.get();
+      } else {
+        throw e;
+      }
+    }
   }
 
   @Override
diff --git a/codecs-parent/codecs-stream/pom.xml b/codecs-parent/codecs-stream/pom.xml
index 7d18752..1df0b9d 100644
--- a/codecs-parent/codecs-stream/pom.xml
+++ b/codecs-parent/codecs-stream/pom.xml
@@ -50,6 +50,11 @@
       <artifactId>jackson-datatype-jdk8</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.datatype</groupId>
+      <artifactId>jackson-datatype-guava</artifactId>
+      <scope>test</scope>
+    </dependency>
     <dependency>
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
diff --git a/codecs-parent/codecs-stream/src/main/java/org/interledger/codecs/stream/frame/AsnConnectionCloseFrameDataCodec.java b/codecs-parent/codecs-stream/src/main/java/org/interledger/codecs/stream/frame/AsnConnectionCloseFrameDataCodec.java
index 52dbc78..86e3af4 100644
--- a/codecs-parent/codecs-stream/src/main/java/org/interledger/codecs/stream/frame/AsnConnectionCloseFrameDataCodec.java
+++ b/codecs-parent/codecs-stream/src/main/java/org/interledger/codecs/stream/frame/AsnConnectionCloseFrameDataCodec.java
@@ -25,7 +25,7 @@ import org.interledger.encoding.asn.codecs.AsnSequenceCodec;
 import org.interledger.encoding.asn.codecs.AsnSizeConstraint;
 import org.interledger.encoding.asn.codecs.AsnUint8Codec;
 import org.interledger.stream.frames.ConnectionCloseFrame;
-import org.interledger.stream.frames.ErrorCode;
+import org.interledger.stream.frames.ErrorCodes;
 
 import java.util.Optional;
 
@@ -44,14 +44,14 @@ public class AsnConnectionCloseFrameDataCodec extends AsnSequenceCodec<Connectio
   @Override
   public ConnectionCloseFrame decode() {
     return ConnectionCloseFrame.builder()
-        .errorCode(ErrorCode.of(getValueAt(0)))
+        .errorCode(ErrorCodes.of(getValueAt(0)))
         .errorMessage(Optional.<String>ofNullable(getValueAt(1)).filter($ -> !"".equals($)))
         .build();
   }
 
   @Override
   public void encode(ConnectionCloseFrame value) {
-    setValueAt(0, value.errorCode().getCode());
+    setValueAt(0, value.errorCode().code());
     setValueAt(1, value.errorMessage().orElse(""));
   }
 }
diff --git a/codecs-parent/codecs-stream/src/main/java/org/interledger/codecs/stream/frame/AsnStreamCloseFrameDataCodec.java b/codecs-parent/codecs-stream/src/main/java/org/interledger/codecs/stream/frame/AsnStreamCloseFrameDataCodec.java
index 94843a8..ec7fc3d 100644
--- a/codecs-parent/codecs-stream/src/main/java/org/interledger/codecs/stream/frame/AsnStreamCloseFrameDataCodec.java
+++ b/codecs-parent/codecs-stream/src/main/java/org/interledger/codecs/stream/frame/AsnStreamCloseFrameDataCodec.java
@@ -25,7 +25,7 @@ import org.interledger.encoding.asn.codecs.AsnSizeConstraint;
 import org.interledger.encoding.asn.codecs.AsnUint8Codec;
 import org.interledger.encoding.asn.codecs.AsnUintCodecUL;
 import org.interledger.encoding.asn.codecs.AsnUtf8StringCodec;
-import org.interledger.stream.frames.ErrorCode;
+import org.interledger.stream.frames.ErrorCodes;
 import org.interledger.stream.frames.StreamCloseFrame;
 
 public class AsnStreamCloseFrameDataCodec extends AsnSequenceCodec<StreamCloseFrame> {
@@ -45,7 +45,7 @@ public class AsnStreamCloseFrameDataCodec extends AsnSequenceCodec<StreamCloseFr
   public StreamCloseFrame decode() {
     return StreamCloseFrame.builder()
         .streamId(getValueAt(0))
-        .errorCode(ErrorCode.of(getValueAt(1)))
+        .errorCode(ErrorCodes.of(getValueAt(1)))
         .errorMessage(getValueAt(2))
         .build();
   }
@@ -53,7 +53,7 @@ public class AsnStreamCloseFrameDataCodec extends AsnSequenceCodec<StreamCloseFr
   @Override
   public void encode(StreamCloseFrame value) {
     setValueAt(0, value.streamId());
-    setValueAt(1, value.errorCode().getCode());
+    setValueAt(1, value.errorCode().code());
     setValueAt(2, value.errorMessage());
   }
 }
diff --git a/codecs-parent/codecs-stream/src/main/java/org/interledger/codecs/stream/frame/AsnStreamMoneyBlockedFrameDataCodec.java b/codecs-parent/codecs-stream/src/main/java/org/interledger/codecs/stream/frame/AsnStreamMoneyBlockedFrameDataCodec.java
index 36d335b..aade75a 100644
--- a/codecs-parent/codecs-stream/src/main/java/org/interledger/codecs/stream/frame/AsnStreamMoneyBlockedFrameDataCodec.java
+++ b/codecs-parent/codecs-stream/src/main/java/org/interledger/codecs/stream/frame/AsnStreamMoneyBlockedFrameDataCodec.java
@@ -24,6 +24,8 @@ import org.interledger.encoding.asn.codecs.AsnSequenceCodec;
 import org.interledger.encoding.asn.codecs.AsnUintCodecUL;
 import org.interledger.stream.frames.StreamMoneyBlockedFrame;
 
+import com.google.common.primitives.UnsignedLong;
+
 public class AsnStreamMoneyBlockedFrameDataCodec extends AsnSequenceCodec<StreamMoneyBlockedFrame> {
 
   /**
@@ -32,7 +34,7 @@ public class AsnStreamMoneyBlockedFrameDataCodec extends AsnSequenceCodec<Stream
   public AsnStreamMoneyBlockedFrameDataCodec() {
     super(
         new AsnUintCodecUL(),
-        new AsnUintCodecUL(),
+        new AsnUintCodecUL(UnsignedLong.MAX_VALUE),
         new AsnUintCodecUL()
     );
   }
diff --git a/codecs-parent/codecs-stream/src/main/java/org/interledger/codecs/stream/frame/AsnStreamMoneyMaxFrameDataCodec.java b/codecs-parent/codecs-stream/src/main/java/org/interledger/codecs/stream/frame/AsnStreamMoneyMaxFrameDataCodec.java
index c1c3aab..0c076cb 100644
--- a/codecs-parent/codecs-stream/src/main/java/org/interledger/codecs/stream/frame/AsnStreamMoneyMaxFrameDataCodec.java
+++ b/codecs-parent/codecs-stream/src/main/java/org/interledger/codecs/stream/frame/AsnStreamMoneyMaxFrameDataCodec.java
@@ -24,6 +24,8 @@ import org.interledger.encoding.asn.codecs.AsnSequenceCodec;
 import org.interledger.encoding.asn.codecs.AsnUintCodecUL;
 import org.interledger.stream.frames.StreamMoneyMaxFrame;
 
+import com.google.common.primitives.UnsignedLong;
+
 public class AsnStreamMoneyMaxFrameDataCodec extends AsnSequenceCodec<StreamMoneyMaxFrame> {
 
   /**
@@ -32,7 +34,7 @@ public class AsnStreamMoneyMaxFrameDataCodec extends AsnSequenceCodec<StreamMone
   public AsnStreamMoneyMaxFrameDataCodec() {
     super(
         new AsnUintCodecUL(),
-        new AsnUintCodecUL(),
+        new AsnUintCodecUL(UnsignedLong.MAX_VALUE),
         new AsnUintCodecUL()
     );
   }
diff --git a/stream-parent/stream-core/src/main/java/org/interledger/stream/frames/ErrorCode.java b/stream-parent/stream-core/src/main/java/org/interledger/stream/frames/ErrorCode.java
index d8172c7..54bc73b 100644
--- a/stream-parent/stream-core/src/main/java/org/interledger/stream/frames/ErrorCode.java
+++ b/stream-parent/stream-core/src/main/java/org/interledger/stream/frames/ErrorCode.java
@@ -1,132 +1,30 @@
 package org.interledger.stream.frames;
 
-import static org.interledger.stream.frames.ErrorCodeConstants.APPLICATION_ERROR;
-import static org.interledger.stream.frames.ErrorCodeConstants.ENDPOINT_BUSY;
-import static org.interledger.stream.frames.ErrorCodeConstants.FLOW_CONTROL_ERROR;
-import static org.interledger.stream.frames.ErrorCodeConstants.FRAME_FORMAT_ERROR;
-import static org.interledger.stream.frames.ErrorCodeConstants.INTERNAL_ERROR;
-import static org.interledger.stream.frames.ErrorCodeConstants.NO_ERROR;
-import static org.interledger.stream.frames.ErrorCodeConstants.PROTOCOL_VIOLATION;
-import static org.interledger.stream.frames.ErrorCodeConstants.STREAM_ID_ERROR;
-import static org.interledger.stream.frames.ErrorCodeConstants.STREAM_STATE_ERROR;
-
-import java.util.Objects;
-import java.util.StringJoiner;
+import org.interledger.core.Immutable;
 
 /**
- * Error codes are sent in StreamClose and ConnectionClose frames to indicate what caused the stream or connection to be
- * closed.
+ * Defines an error code for the purposes of the STREAM protocol. Error codes are are sent in {@link StreamCloseFrame}
+ * and {@link ConnectionCloseFrame} to indicate what caused the stream or connection to be closed.
  */
-public enum ErrorCode {
-
-  /**
-   * Indicates the stream or connection closed normally.
-   */
-  NoError(NO_ERROR, "The stream or connection closed normally."),
-
-  /**
-   * The endpoint encountered an unexpected error.
-   */
-  InternalError(INTERNAL_ERROR, "The endpoint encountered an unexpected error."),
-
-  /**
-   * The endpoint is temporarily overloaded and unable to process the packet.
-   */
-  EndpointBusy(ENDPOINT_BUSY, "The endpoint is temporarily overloaded and unable to process the packet."),
-
-  /**
-   * The other endpoint exceeded the flow control limits advertised.
-   */
-  FlowControlError(FLOW_CONTROL_ERROR, "The other endpoint exceeded the flow control limits advertised."),
-
-  /**
-   * The other endpoint opened more streams than allowed.
-   */
-  StreamIdError(STREAM_ID_ERROR, "The other endpoint opened more streams than allowed."),
-
-  /**
-   * The other endpoint sent frames for a stream that is already closed.
-   */
-  StreamStateError(STREAM_STATE_ERROR, "The other endpoint sent frames for a stream that is already closed."),
+@Immutable
+public interface ErrorCode {
 
-  /**
-   * The other endpoint sent a frame with invalid syntax.
-   */
-  FrameFormatError(FRAME_FORMAT_ERROR, "The other endpoint sent a frame with invalid syntax."),
-
-  /**
-   * The other endpoint sent invalid data or otherwise violated the protocol.
-   */
-  ProtocolViolation(PROTOCOL_VIOLATION, "The other endpoint sent invalid data or otherwise violated the protocol."),
+  static ErrorCodeBuilder builder() {
+    return new ErrorCodeBuilder();
+  }
 
   /**
-   * The application using STREAM closed the stream or connection with an error.
+   * The STREAM error code.
+   *
+   * @return A {@link short} representing the error code.
    */
-  ApplicationError(APPLICATION_ERROR, "The application using STREAM closed the stream or connection with an error.");
-
-  private final short code;
-  private final String description;
-
-  ErrorCode(short code, String description) {
-    this.code = Objects.requireNonNull(code);
-    this.description = Objects.requireNonNull(description);
-  }
+  short code();
 
   /**
-   * Helper method to construct an instance of {@link ErrorCode}.
-   *
-   * @param code The definitive identifier of the error.
+   * The description of the error corresponding to {@link #code()}.
    *
-   * @return An {@link ErrorCode}
+   * @return A {@link String} containing a description of this error code.
    */
-  public static ErrorCode of(final short code) {
-    switch (code) {
-      case NO_ERROR: {
-        return NoError;
-      }
-      case INTERNAL_ERROR: {
-        return InternalError;
-      }
-      case ENDPOINT_BUSY: {
-        return EndpointBusy;
-      }
-      case FLOW_CONTROL_ERROR: {
-        return FlowControlError;
-      }
-      case STREAM_ID_ERROR: {
-        return StreamIdError;
-      }
-      case STREAM_STATE_ERROR: {
-        return StreamStateError;
-      }
-      case FRAME_FORMAT_ERROR: {
-        return FrameFormatError;
-      }
-      case PROTOCOL_VIOLATION: {
-        return ProtocolViolation;
-      }
-      case APPLICATION_ERROR: {
-        return ApplicationError;
-      }
-      default: {
-        throw new RuntimeException("Invalid Stream Error code: " + code);
-      }
-    }
-  }
+  String description();
 
-  public short getCode() {
-    return code;
-  }
-
-  public String getDescription() {
-    return description;
-  }
-
-  @Override
-  public String toString() {
-    return new StringJoiner(", ", ErrorCode.class.getSimpleName() + "[", "]")
-        .add("code=" + code)
-        .add("description='" + description + "'")
-        .toString();
-  }
 }
diff --git a/stream-parent/stream-core/src/main/java/org/interledger/stream/frames/ErrorCodeConstants.java b/stream-parent/stream-core/src/main/java/org/interledger/stream/frames/ErrorCodeConstants.java
index 067beba..2d572ef 100644
--- a/stream-parent/stream-core/src/main/java/org/interledger/stream/frames/ErrorCodeConstants.java
+++ b/stream-parent/stream-core/src/main/java/org/interledger/stream/frames/ErrorCodeConstants.java
@@ -2,6 +2,7 @@ package org.interledger.stream.frames;
 
 public interface ErrorCodeConstants {
 
+  short UNSUPPORTED_ERROR = (short) 0x0;
   short NO_ERROR = (short) 0x01;
   short INTERNAL_ERROR = (short) 0x02;
   short ENDPOINT_BUSY = (short) 0x03;
@@ -11,5 +12,4 @@ public interface ErrorCodeConstants {
   short FRAME_FORMAT_ERROR = (short) 0x07;
   short PROTOCOL_VIOLATION = (short) 0x08;
   short APPLICATION_ERROR = (short) 0x09;
-
 }
diff --git a/stream-parent/stream-core/src/main/java/org/interledger/stream/frames/ErrorCodes.java b/stream-parent/stream-core/src/main/java/org/interledger/stream/frames/ErrorCodes.java
new file mode 100644
index 0000000..59d25cf
--- /dev/null
+++ b/stream-parent/stream-core/src/main/java/org/interledger/stream/frames/ErrorCodes.java
@@ -0,0 +1,143 @@
+package org.interledger.stream.frames;
+
+import static org.interledger.stream.frames.ErrorCodeConstants.APPLICATION_ERROR;
+import static org.interledger.stream.frames.ErrorCodeConstants.ENDPOINT_BUSY;
+import static org.interledger.stream.frames.ErrorCodeConstants.FLOW_CONTROL_ERROR;
+import static org.interledger.stream.frames.ErrorCodeConstants.FRAME_FORMAT_ERROR;
+import static org.interledger.stream.frames.ErrorCodeConstants.INTERNAL_ERROR;
+import static org.interledger.stream.frames.ErrorCodeConstants.NO_ERROR;
+import static org.interledger.stream.frames.ErrorCodeConstants.PROTOCOL_VIOLATION;
+import static org.interledger.stream.frames.ErrorCodeConstants.STREAM_ID_ERROR;
+import static org.interledger.stream.frames.ErrorCodeConstants.STREAM_STATE_ERROR;
+import static org.interledger.stream.frames.ErrorCodeConstants.UNSUPPORTED_ERROR;
+
+import java.util.Objects;
+import java.util.StringJoiner;
+
+/**
+ * Error codes are sent in StreamClose and ConnectionClose frames to indicate what caused the stream or connection to be
+ * closed.
+ */
+public enum ErrorCodes implements ErrorCode {
+
+  /**
+   * The application using STREAM closed the stream or connection with an error.
+   */
+  UnsupportedError(UNSUPPORTED_ERROR, "An unsupported error code was supplied."),
+
+  /**
+   * Indicates the stream or connection closed normally.
+   */
+  NoError(NO_ERROR, "The stream or connection closed normally."),
+
+  /**
+   * The endpoint encountered an unexpected error.
+   */
+  InternalError(INTERNAL_ERROR, "The endpoint encountered an unexpected error."),
+
+  /**
+   * The endpoint is temporarily overloaded and unable to process the packet.
+   */
+  EndpointBusy(ENDPOINT_BUSY, "The endpoint is temporarily overloaded and unable to process the packet."),
+
+  /**
+   * The other endpoint exceeded the flow control limits advertised.
+   */
+  FlowControlError(FLOW_CONTROL_ERROR, "The other endpoint exceeded the flow control limits advertised."),
+
+  /**
+   * The other endpoint opened more streams than allowed.
+   */
+  StreamIdError(STREAM_ID_ERROR, "The other endpoint opened more streams than allowed."),
+
+  /**
+   * The other endpoint sent frames for a stream that is already closed.
+   */
+  StreamStateError(STREAM_STATE_ERROR, "The other endpoint sent frames for a stream that is already closed."),
+
+  /**
+   * The other endpoint sent a frame with invalid syntax.
+   */
+  FrameFormatError(FRAME_FORMAT_ERROR, "The other endpoint sent a frame with invalid syntax."),
+
+  /**
+   * The other endpoint sent invalid data or otherwise violated the protocol.
+   */
+  ProtocolViolation(PROTOCOL_VIOLATION, "The other endpoint sent invalid data or otherwise violated the protocol."),
+
+  /**
+   * The application using STREAM closed the stream or connection with an error.
+   */
+  ApplicationError(APPLICATION_ERROR, "The application using STREAM closed the stream or connection with an error.");
+
+  private final short code;
+  private final String description;
+
+  ErrorCodes(final short code, final String description) {
+    this.code = code;
+    this.description = Objects.requireNonNull(description);
+  }
+
+  /**
+   * Helper method to construct an instance of {@link ErrorCode}.
+   *
+   * @param code The definitive identifier of the error.
+   *
+   * @return An {@link ErrorCode}
+   */
+  public static ErrorCode of(final short code) {
+    switch (code) {
+      case NO_ERROR: {
+        return ErrorCode.builder().from(NoError).build();
+      }
+      case INTERNAL_ERROR: {
+        return ErrorCode.builder().from(InternalError).build();
+      }
+      case ENDPOINT_BUSY: {
+        return ErrorCode.builder().from(EndpointBusy).build();
+      }
+      case FLOW_CONTROL_ERROR: {
+        return ErrorCode.builder().from(FlowControlError).build();
+      }
+      case STREAM_ID_ERROR: {
+        return ErrorCode.builder().from(StreamIdError).build();
+      }
+      case STREAM_STATE_ERROR: {
+        return ErrorCode.builder().from(StreamStateError).build();
+      }
+      case FRAME_FORMAT_ERROR: {
+        return ErrorCode.builder().from(FrameFormatError).build();
+      }
+      case PROTOCOL_VIOLATION: {
+        return ErrorCode.builder().from(ProtocolViolation).build();
+      }
+      case APPLICATION_ERROR: {
+        return ErrorCode.builder().from(ApplicationError).build();
+      }
+      default: {
+        return ErrorCode.builder()
+            .from(UnsupportedError)
+            .code(code)
+            .build();
+      }
+    }
+  }
+
+  @Override
+  public short code() {
+    return code;
+  }
+
+  @Override
+  public String description() {
+    return description;
+  }
+
+  @Override
+  public String toString() {
+    return new StringJoiner(", ", ErrorCodes.class.getSimpleName() + "[", "]")
+        .add("code=" + code)
+        .add("description='" + description + "'")
+        .toString();
+  }
+}
diff --git a/stream-parent/stream-core/src/main/java/org/interledger/stream/frames/StreamCloseFrame.java b/stream-parent/stream-core/src/main/java/org/interledger/stream/frames/StreamCloseFrame.java
index 5f6c840..83590cb 100644
--- a/stream-parent/stream-core/src/main/java/org/interledger/stream/frames/StreamCloseFrame.java
+++ b/stream-parent/stream-core/src/main/java/org/interledger/stream/frames/StreamCloseFrame.java
@@ -58,7 +58,7 @@ public interface StreamCloseFrame extends StreamFrame {
   /**
    * Machine-readable {@link ErrorCode} indicating why the Stream was closed.
    *
-   * @return A {@link ErrorCode}.
+   * @return An {@link ErrorCode}.
    */
   ErrorCode errorCode();
 
diff --git a/stream-parent/stream-receiver/src/main/java/org/interledger/stream/receiver/StatelessStreamReceiver.java b/stream-parent/stream-receiver/src/main/java/org/interledger/stream/receiver/StatelessStreamReceiver.java
index 0fe2340..7c9e943 100644
--- a/stream-parent/stream-receiver/src/main/java/org/interledger/stream/receiver/StatelessStreamReceiver.java
+++ b/stream-parent/stream-receiver/src/main/java/org/interledger/stream/receiver/StatelessStreamReceiver.java
@@ -20,7 +20,7 @@ import org.interledger.stream.StreamUtils;
 import org.interledger.stream.crypto.StreamEncryptionService;
 import org.interledger.stream.frames.ConnectionAssetDetailsFrame;
 import org.interledger.stream.frames.ConnectionCloseFrame;
-import org.interledger.stream.frames.ErrorCode;
+import org.interledger.stream.frames.ErrorCodes;
 import org.interledger.stream.frames.StreamFrame;
 import org.interledger.stream.frames.StreamFrameType;
 import org.interledger.stream.frames.StreamMoneyFrame;
@@ -129,7 +129,7 @@ public class StatelessStreamReceiver implements StreamReceiver {
           streamPacket.sequence());
       // If the sequence it too high, we should close the Connection.
       responseFrames.add(ConnectionCloseFrame.builder()
-          .errorCode(ErrorCode.ProtocolViolation)
+          .errorCode(ErrorCodes.ProtocolViolation)
           .errorMessage("Sequence number was to too high for safe encryption")
           .build());
     }


Test patch:
diff --git a/codecs-parent/codecs-framework/src/test/java/org/interledger/encoding/asn/codecs/AsnUintCodecULTest.java b/codecs-parent/codecs-framework/src/test/java/org/interledger/encoding/asn/codecs/AsnUintCodecULTest.java
new file mode 100644
index 0000000..44c38d2
--- /dev/null
+++ b/codecs-parent/codecs-framework/src/test/java/org/interledger/encoding/asn/codecs/AsnUintCodecULTest.java
@@ -0,0 +1,48 @@
+package org.interledger.encoding.asn.codecs;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.google.common.primitives.UnsignedLong;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.math.BigInteger;
+
+/**
+ * Unit tests for {@link AsnUintCodecUL}.
+ */
+public class AsnUintCodecULTest {
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private AsnUintCodecUL codec;
+
+  @Test
+  public void constructWithNullValues() {
+    expectedException.expect(RuntimeException.class);
+    new AsnUintCodecUL(null);
+  }
+
+  @Test
+  public void decode() {
+    codec = new AsnUintCodecUL();
+    codec.setBytes(new byte[]{1});
+    assertThat(codec.decode()).isEqualTo(UnsignedLong.ONE);
+  }
+
+  @Test
+  public void decodeValueTooLarge(){
+    codec = new AsnUintCodecUL(UnsignedLong.ONE);
+    byte[] bytes = new BigInteger("18446744073709551616").toByteArray();
+    codec.setBytes(bytes);
+    assertThat(codec.decode()).isEqualTo(UnsignedLong.ONE);
+  }
+  @Test
+  public void encode() {
+    codec = new AsnUintCodecUL();
+    codec.encode(UnsignedLong.ONE);
+    assertThat(codec.getBytes()).isEqualTo(new byte[] {1});
+  }
+}
diff --git a/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/StreamPacketFixturesTest.java b/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/StreamPacketFixturesTest.java
new file mode 100644
index 0000000..e3db7f4
--- /dev/null
+++ b/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/StreamPacketFixturesTest.java
@@ -0,0 +1,245 @@
+package org.interledger.codecs.stream;
+
+/*-
+ * ========================LICENSE_START=================================
+ * Crypto Conditions
+ * %%
+ * Copyright (C) 2016 - 2018 Ripple Labs
+ * %%
+ * Licensed 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.
+ * =========================LICENSE_END==================================
+ */
+
+import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
+
+import org.interledger.codecs.stream.frame.helpers.StreamFrameFixture;
+import org.interledger.codecs.stream.frame.helpers.StreamTestFixture;
+import org.interledger.core.InterledgerAddress;
+import org.interledger.core.InterledgerPacketType;
+import org.interledger.stream.Denomination;
+import org.interledger.stream.StreamPacket;
+import org.interledger.stream.frames.ConnectionAssetDetailsFrame;
+import org.interledger.stream.frames.ConnectionCloseFrame;
+import org.interledger.stream.frames.ConnectionDataBlockedFrame;
+import org.interledger.stream.frames.ConnectionDataMaxFrame;
+import org.interledger.stream.frames.ConnectionMaxStreamIdFrame;
+import org.interledger.stream.frames.ConnectionNewAddressFrame;
+import org.interledger.stream.frames.ConnectionStreamIdBlockedFrame;
+import org.interledger.stream.frames.ErrorCodes;
+import org.interledger.stream.frames.StreamCloseFrame;
+import org.interledger.stream.frames.StreamDataBlockedFrame;
+import org.interledger.stream.frames.StreamDataFrame;
+import org.interledger.stream.frames.StreamDataMaxFrame;
+import org.interledger.stream.frames.StreamFrame;
+import org.interledger.stream.frames.StreamFrameType;
+import org.interledger.stream.frames.StreamMoneyBlockedFrame;
+import org.interledger.stream.frames.StreamMoneyFrame;
+import org.interledger.stream.frames.StreamMoneyMaxFrame;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.guava.GuavaModule;
+import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
+import com.google.common.primitives.UnsignedLong;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * <p>This class tests the Java implementation of STREAM based on a set of pre-computed and validated test
+ * vectors found in the file `ValidStreamFrameVectorsTest.json`.</p>
+ */
+@RunWith(Parameterized.class)
+public class StreamPacketFixturesTest {
+
+  private StreamTestFixture streamTestFixture;
+
+  public StreamPacketFixturesTest(StreamTestFixture streamTestFixture) {
+    this.streamTestFixture = Objects.requireNonNull(streamTestFixture);
+  }
+
+  /**
+   * Loads a list of tests based on the json-encoded test vector files.
+   */
+  @Parameters(name = "Test Vector {index}: {0}")
+  public static Collection<StreamTestFixture> testVectorData() throws Exception {
+
+    final ObjectMapper objectMapper = new ObjectMapper();
+    objectMapper.registerModule(new Jdk8Module());
+    objectMapper.registerModule(new GuavaModule());
+    objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+
+    Path path = Paths
+        .get(StreamPacketFixturesTest.class.getClassLoader().getResource("StreamPacketFixtures.json").toURI());
+    Stream<String> lines = Files.lines(path);
+    String data = lines.collect(Collectors.joining("\n"));
+    lines.close();
+
+    List<StreamTestFixture> vectors = objectMapper.readValue(data, new TypeReference<List<StreamTestFixture>>() {
+    });
+
+    return vectors;
+  }
+
+  /**
+   * This test parses the conditionBinary, serializes it as a URI, and validates that the generated URI matches
+   * "conditionUri" from the test vector.
+   */
+  @Test
+  public void testParseConditionBinary() throws IOException {
+
+    StreamTestFixture fixture = this.streamTestFixture;
+
+    // Base64-encoded Bytes...
+    String wantBuffer = fixture.buffer();
+
+    StreamPacket wantPacket = StreamPacket.builder()
+        .sequence(UnsignedLong.valueOf(fixture.packet().sequence()))
+        .interledgerPacketType(InterledgerPacketType.fromCode(fixture.packet().packetType()))
+        .prepareAmount(UnsignedLong.valueOf(fixture.packet().amount()))
+        .frames(
+            fixture.packet().frames().stream()
+                .map(this::fromJson)
+                .collect(Collectors.toList())
+        )
+        .build();
+
+    // Deserialize...
+    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Base64.getDecoder().decode(fixture.buffer()));
+    StreamPacket packet = StreamCodecContextFactory.oer().read(StreamPacket.class, byteArrayInputStream);
+
+    assertThat(packet).isEqualTo(wantPacket);
+
+    if (fixture.decodeOnly().isPresent() && fixture.decodeOnly().get()) {
+      return;
+    }
+
+    // Serialize...
+    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+    StreamCodecContextFactory.oer().write(wantPacket, byteArrayOutputStream);
+
+    assertThat(Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray())).isEqualTo(wantBuffer);
+  }
+
+
+  private StreamFrame fromJson(final StreamFrameFixture fixture) {
+
+    StreamFrameType streamFrameType = StreamFrameType.fromCode(fixture.type());
+
+    switch (streamFrameType) {
+      case ConnectionClose: {
+        return ConnectionCloseFrame.builder()
+            .errorCode(ErrorCodes.of(fixture.errorCode().get()))
+            .errorMessage(fixture.errorMessage())
+            .build();
+      }
+      case ConnectionNewAddress: {
+        return ConnectionNewAddressFrame.builder()
+            .sourceAddress(InterledgerAddress.of(fixture.sourceAccount().get()))
+            .build();
+      }
+      case ConnectionDataMax: {
+        return ConnectionDataMaxFrame.builder()
+            .maxOffset(UnsignedLong.valueOf(fixture.maxOffset().get()))
+            .build();
+      }
+      case ConnectionDataBlocked: {
+        return ConnectionDataBlockedFrame.builder()
+            .maxOffset(UnsignedLong.valueOf(fixture.maxOffset().get()))
+            .build();
+      }
+      case ConnectionMaxStreamId: {
+        return ConnectionMaxStreamIdFrame.builder()
+            .maxStreamId(UnsignedLong.valueOf(fixture.maxStreamId().get()))
+            .build();
+      }
+      case ConnectionStreamIdBlocked: {
+        return ConnectionStreamIdBlockedFrame.builder()
+            .maxStreamId(UnsignedLong.valueOf(fixture.maxStreamId().get()))
+            .build();
+      }
+      case StreamClose: {
+        return StreamCloseFrame.builder()
+            .streamId(UnsignedLong.valueOf(fixture.streamId().get()))
+            .errorCode(ErrorCodes.of(fixture.errorCode().get()))
+            .errorMessage(fixture.errorMessage().get())
+            .build();
+      }
+      case StreamMoney: {
+        return StreamMoneyFrame.builder()
+            .streamId(UnsignedLong.valueOf(fixture.streamId().get()))
+            .shares(UnsignedLong.valueOf(fixture.shares().get()))
+            .build();
+      }
+      case StreamMoneyMax: {
+        return StreamMoneyMaxFrame.builder()
+            .streamId(UnsignedLong.valueOf(fixture.streamId().get()))
+            .totalReceived(UnsignedLong.valueOf(fixture.totalReceived().get()))
+            .receiveMax(UnsignedLong.valueOf(fixture.receiveMax().get()))
+            .build();
+      }
+      case StreamMoneyBlocked: {
+        return StreamMoneyBlockedFrame.builder()
+            .streamId(UnsignedLong.valueOf(fixture.streamId().get()))
+            .sendMax(UnsignedLong.valueOf(fixture.sendMax().get()))
+            .totalSent(UnsignedLong.valueOf(fixture.totalSent().get()))
+            .build();
+      }
+      case StreamData: {
+        return StreamDataFrame.builder()
+            .streamId(UnsignedLong.valueOf(fixture.streamId().get()))
+            .offset(UnsignedLong.valueOf(fixture.offset().get()))
+            .data(Base64.getDecoder().decode(fixture.data().get()))
+            .build();
+      }
+      case StreamDataMax: {
+        return StreamDataMaxFrame.builder()
+            .streamId(UnsignedLong.valueOf(fixture.streamId().get()))
+            .maxOffset(UnsignedLong.valueOf(fixture.maxOffset().get()))
+            .build();
+      }
+      case StreamDataBlocked: {
+        return StreamDataBlockedFrame.builder()
+            .streamId(UnsignedLong.valueOf(fixture.streamId().get()))
+            .maxOffset(UnsignedLong.valueOf(fixture.maxOffset().get()))
+            .build();
+      }
+      case ConnectionAssetDetails: {
+        return ConnectionAssetDetailsFrame.builder().sourceDenomination(
+            Denomination.builder()
+                .assetCode(fixture.sourceAssetCode().get())
+                .assetScale(Short.valueOf(fixture.sourceAssetScale().get()))
+                .build())
+            .build();
+      }
+      default: {
+        throw new RuntimeException("Unsupported Frame:" + streamFrameType);
+      }
+    }
+  }
+}
diff --git a/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/AsnConnectionCloseFrameCodecTest.java b/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/AsnConnectionCloseFrameCodecTest.java
index 9ad7f4e..bc51bfa 100644
--- a/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/AsnConnectionCloseFrameCodecTest.java
+++ b/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/AsnConnectionCloseFrameCodecTest.java
@@ -22,6 +22,7 @@ package org.interledger.codecs.stream.frame;
 
 import org.interledger.stream.frames.ConnectionCloseFrame;
 import org.interledger.stream.frames.ErrorCode;
+import org.interledger.stream.frames.ErrorCodes;
 
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -51,13 +52,13 @@ public class AsnConnectionCloseFrameCodecTest extends AbstractAsnFrameCodecTest<
         // empty message
         {
             ConnectionCloseFrame.builder()
-                .errorCode(ErrorCode.NoError)
+                .errorCode(ErrorCode.builder().from(ErrorCodes.NoError).build())
                 .build()
         },
         // full frame
         {
             ConnectionCloseFrame.builder()
-                .errorCode(ErrorCode.NoError)
+                .errorCode(ErrorCode.builder().from(ErrorCodes.NoError).build())
                 .errorMessage("present")
                 .build()
         },
diff --git a/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/AsnStreamCloseFrameCodecTest.java b/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/AsnStreamCloseFrameCodecTest.java
index 397b2d2..5cf6fa6 100644
--- a/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/AsnStreamCloseFrameCodecTest.java
+++ b/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/AsnStreamCloseFrameCodecTest.java
@@ -21,6 +21,7 @@
 package org.interledger.codecs.stream.frame;
 
 import org.interledger.stream.frames.ErrorCode;
+import org.interledger.stream.frames.ErrorCodes;
 import org.interledger.stream.frames.StreamCloseFrame;
 
 import com.google.common.primitives.UnsignedLong;
@@ -52,7 +53,7 @@ public class AsnStreamCloseFrameCodecTest extends AbstractAsnFrameCodecTest<Stre
         {
             StreamCloseFrame.builder()
                 .streamId(UnsignedLong.ZERO)
-                .errorCode(ErrorCode.NoError)
+                .errorCode(ErrorCode.builder().from(ErrorCodes.NoError).build())
                 .errorMessage("the error")
                 .build()
         },
@@ -60,7 +61,7 @@ public class AsnStreamCloseFrameCodecTest extends AbstractAsnFrameCodecTest<Stre
         {
             StreamCloseFrame.builder()
                 .streamId(UnsignedLong.ONE)
-                .errorCode(ErrorCode.NoError)
+                .errorCode(ErrorCode.builder().from(ErrorCodes.NoError).build())
                 .errorMessage("the error")
                 .build()
         },
@@ -68,7 +69,7 @@ public class AsnStreamCloseFrameCodecTest extends AbstractAsnFrameCodecTest<Stre
         {
             StreamCloseFrame.builder()
                 .streamId(UnsignedLong.valueOf(10L))
-                .errorCode(ErrorCode.NoError)
+                .errorCode(ErrorCode.builder().from(ErrorCodes.NoError).build())
                 .errorMessage("the error")
                 .build()
         },
@@ -76,7 +77,7 @@ public class AsnStreamCloseFrameCodecTest extends AbstractAsnFrameCodecTest<Stre
         {
             StreamCloseFrame.builder()
                 .streamId(UnsignedLong.MAX_VALUE.minus(UnsignedLong.ONE))
-                .errorCode(ErrorCode.NoError)
+                .errorCode(ErrorCode.builder().from(ErrorCodes.NoError).build())
                 .errorMessage("the error")
                 .build()
         },
@@ -84,7 +85,7 @@ public class AsnStreamCloseFrameCodecTest extends AbstractAsnFrameCodecTest<Stre
         {
             StreamCloseFrame.builder()
                 .streamId(UnsignedLong.MAX_VALUE)
-                .errorCode(ErrorCode.NoError)
+                .errorCode(ErrorCode.builder().from(ErrorCodes.NoError).build())
                 .errorMessage("the error")
                 .build()
         },
@@ -92,14 +93,14 @@ public class AsnStreamCloseFrameCodecTest extends AbstractAsnFrameCodecTest<Stre
         {
             StreamCloseFrame.builder()
                 .streamId(UnsignedLong.valueOf(10L))
-                .errorCode(ErrorCode.NoError)
+                .errorCode(ErrorCode.builder().from(ErrorCodes.NoError).build())
                 .build()
         },
         // longErrorMessage
         {
             StreamCloseFrame.builder()
                 .streamId(UnsignedLong.ZERO)
-                .errorCode(ErrorCode.NoError)
+                .errorCode(ErrorCode.builder().from(ErrorCodes.NoError).build())
                 .errorMessage("the error the error the error the error the error the error the error the error the "
                     + "error the error the error the error the error the error the error the error the error the error "
                     + "the error the error the error the error the error the error the error the error the error the "
@@ -112,7 +113,7 @@ public class AsnStreamCloseFrameCodecTest extends AbstractAsnFrameCodecTest<Stre
         {
             StreamCloseFrame.builder()
                 .streamId(UnsignedLong.ZERO)
-                .errorCode(ErrorCode.ProtocolViolation)
+                .errorCode(ErrorCode.builder().from(ErrorCodes.ProtocolViolation).build())
                 .errorMessage("the error")
                 .build()
         },
diff --git a/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/AsnStreamMoneyMaxFrameCodecTest.java b/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/AsnStreamMoneyMaxFrameCodecTest.java
index 01ed2bb..3308858 100644
--- a/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/AsnStreamMoneyMaxFrameCodecTest.java
+++ b/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/AsnStreamMoneyMaxFrameCodecTest.java
@@ -20,13 +20,22 @@
 
 package org.interledger.codecs.stream.frame;
 
+import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
+
+import org.interledger.codecs.stream.StreamCodecContextFactory;
+import org.interledger.stream.frames.StreamFrameType;
+import org.interledger.stream.frames.StreamMoneyBlockedFrame;
 import org.interledger.stream.frames.StreamMoneyMaxFrame;
 
 import com.google.common.primitives.UnsignedLong;
+import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
 
@@ -171,4 +180,86 @@ public class AsnStreamMoneyMaxFrameCodecTest extends AbstractAsnFrameCodecTest<S
     });
   }
 
+  @Test
+  public void convertMaxAmountThatIsTooLargeToDefault() throws IOException {
+    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+
+    // StreamFrametype
+    byteArrayOutputStream.write(StreamFrameType.StreamMoneyMax.code());
+
+    // numSequence bytes for the rest per OER
+    byteArrayOutputStream.write(14);
+
+    // streamId
+    byteArrayOutputStream.write(1);
+    byteArrayOutputStream.write(122);
+
+    // receiveMax
+    byteArrayOutputStream.write(9);  // Num bytes
+    byteArrayOutputStream.write(1);
+    byteArrayOutputStream.write(2);
+    byteArrayOutputStream.write(3);
+    byteArrayOutputStream.write(4);
+    byteArrayOutputStream.write(5);
+    byteArrayOutputStream.write(6);
+    byteArrayOutputStream.write(7);
+    byteArrayOutputStream.write(8);
+    byteArrayOutputStream.write(9);
+
+    // totalReceived
+    byteArrayOutputStream.write(1);
+    byteArrayOutputStream.write(123);
+
+    final StreamMoneyMaxFrame frame = StreamCodecContextFactory.oer().read(
+        StreamMoneyMaxFrame.class,
+        new ByteArrayInputStream(byteArrayOutputStream.toByteArray())
+    );
+
+    assertThat(frame.streamFrameType()).isEqualTo(StreamFrameType.StreamMoneyMax);
+    assertThat(frame.streamId()).isEqualTo(UnsignedLong.valueOf(122L));
+    assertThat(frame.receiveMax()).isEqualTo(UnsignedLong.MAX_VALUE);
+    assertThat(frame.totalReceived()).isEqualTo(UnsignedLong.valueOf(123L));
+  }
+
+  @Test
+  public void convertMaxMoneyBlockedAmountWithMaxTooLarge() throws IOException {
+    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+
+    // StreamFrametype
+    byteArrayOutputStream.write(StreamFrameType.StreamMoneyBlocked.code());
+
+    // numSequence bytes for the rest per OER
+    byteArrayOutputStream.write(14);
+
+    // streamId
+    byteArrayOutputStream.write(1);
+    byteArrayOutputStream.write(122);
+
+    // receiveMax
+    byteArrayOutputStream.write(9);  // Num bytes
+    byteArrayOutputStream.write(1);
+    byteArrayOutputStream.write(2);
+    byteArrayOutputStream.write(3);
+    byteArrayOutputStream.write(4);
+    byteArrayOutputStream.write(5);
+    byteArrayOutputStream.write(6);
+    byteArrayOutputStream.write(7);
+    byteArrayOutputStream.write(8);
+    byteArrayOutputStream.write(9);
+
+    // totalReceived
+    byteArrayOutputStream.write(1);
+    byteArrayOutputStream.write(123);
+
+    final StreamMoneyBlockedFrame frame = StreamCodecContextFactory.oer().read(
+        StreamMoneyBlockedFrame.class,
+        new ByteArrayInputStream(byteArrayOutputStream.toByteArray())
+    );
+
+    assertThat(frame.streamFrameType()).isEqualTo(StreamFrameType.StreamMoneyBlocked);
+    assertThat(frame.streamId()).isEqualTo(UnsignedLong.valueOf(122L));
+    assertThat(frame.sendMax()).isEqualTo(UnsignedLong.MAX_VALUE);
+    assertThat(frame.totalSent()).isEqualTo(UnsignedLong.valueOf(123L));
+  }
+
 }
diff --git a/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/ValidStreamFrameVectorsTest.java b/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/ValidStreamFrameVectorsTest.java
deleted file mode 100644
index b950cfc..0000000
--- a/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/ValidStreamFrameVectorsTest.java
+++ /dev/null
@@ -1,232 +0,0 @@
-package org.interledger.codecs.stream.frame;
-
-/*-
- * ========================LICENSE_START=================================
- * Crypto Conditions
- * %%
- * Copyright (C) 2016 - 2018 Ripple Labs
- * %%
- * Licensed 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.
- * =========================LICENSE_END==================================
- */
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.interledger.codecs.stream.StreamCodecContextFactory;
-import org.interledger.codecs.stream.frame.helpers.StreamFrameTestVector;
-import org.interledger.core.InterledgerAddress;
-import org.interledger.stream.Denomination;
-import org.interledger.stream.frames.ConnectionAssetDetailsFrame;
-import org.interledger.stream.frames.ConnectionCloseFrame;
-import org.interledger.stream.frames.ConnectionDataBlockedFrame;
-import org.interledger.stream.frames.ConnectionDataMaxFrame;
-import org.interledger.stream.frames.ConnectionMaxStreamIdFrame;
-import org.interledger.stream.frames.ConnectionNewAddressFrame;
-import org.interledger.stream.frames.ConnectionStreamIdBlockedFrame;
-import org.interledger.stream.frames.ErrorCode;
-import org.interledger.stream.frames.StreamCloseFrame;
-import org.interledger.stream.frames.StreamDataBlockedFrame;
-import org.interledger.stream.frames.StreamDataFrame;
-import org.interledger.stream.frames.StreamDataMaxFrame;
-import org.interledger.stream.frames.StreamFrame;
-import org.interledger.stream.frames.StreamFrameType;
-import org.interledger.stream.frames.StreamMoneyBlockedFrame;
-import org.interledger.stream.frames.StreamMoneyFrame;
-import org.interledger.stream.frames.StreamMoneyMaxFrame;
-
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
-import com.google.common.primitives.UnsignedLong;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Base64;
-import java.util.Collection;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-/**
- * <p>This class tests the Java implementation of STREAM based on a set of pre-computed and validated test
- * vectors found in the file `ValidStreamFrameVectorsTest.json`.</p>
- */
-@RunWith(Parameterized.class)
-public class ValidStreamFrameVectorsTest {
-
-  private StreamFrameTestVector streamFrameTestVector;
-
-  public ValidStreamFrameVectorsTest(StreamFrameTestVector streamFrameTestVector) {
-    this.streamFrameTestVector = streamFrameTestVector;
-  }
-
-  /**
-   * Loads a list of tests based on the json-encoded test vector files.
-   */
-  @Parameters(name = "Test Vector {index}: {0}")
-  public static Collection<StreamFrameTestVector> testVectorData() throws Exception {
-
-    final ObjectMapper objectMapper = new ObjectMapper();
-    objectMapper.registerModule(new Jdk8Module());
-    objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
-
-    Path path = Paths.get(ValidStreamFrameVectorsTest.class.getClassLoader()
-        .getResource("ValidStreamFrameVectorsTest.json").toURI());
-    Stream<String> lines = Files.lines(path);
-    String data = lines.collect(Collectors.joining("\n"));
-    lines.close();
-
-    List<StreamFrameTestVector> vectors = objectMapper
-        .readValue(data, new TypeReference<List<StreamFrameTestVector>>() {
-        });
-
-    return vectors;
-  }
-
-  /**
-   * This test parses the conditionBinary, serializes it as a URI, and validates that the generated URI matches
-   * "conditionUri" from the test vector.
-   */
-  @Test
-  public void testParseConditionBinary() throws IOException {
-
-    StreamFrameType type = StreamFrameType.valueOf(streamFrameTestVector.type());
-
-    StreamFrame frameFromTestVectorFile = null;
-    switch (type) {
-      case ConnectionClose: {
-        frameFromTestVectorFile = ConnectionCloseFrame.builder()
-            .errorCode(ErrorCode.of(streamFrameTestVector.errorCode().get()))
-            .errorMessage(streamFrameTestVector.errorMessage())
-            .build();
-        break;
-      }
-      case ConnectionNewAddress: {
-        frameFromTestVectorFile = ConnectionNewAddressFrame.builder()
-            .sourceAddress(InterledgerAddress.of(streamFrameTestVector.sourceAddress().get()))
-            .build();
-        break;
-      }
-      case ConnectionDataMax: {
-        frameFromTestVectorFile = ConnectionDataMaxFrame.builder()
-            .maxOffset(UnsignedLong.valueOf(streamFrameTestVector.maxOffset().get()))
-            .build();
-        break;
-      }
-      case ConnectionDataBlocked: {
-        frameFromTestVectorFile = ConnectionDataBlockedFrame.builder()
-            .maxOffset(UnsignedLong.valueOf(streamFrameTestVector.maxOffset().get()))
-            .build();
-        break;
-      }
-      case ConnectionMaxStreamId: {
-        frameFromTestVectorFile = ConnectionMaxStreamIdFrame.builder()
-            .maxStreamId(UnsignedLong.valueOf(streamFrameTestVector.maxStreamId().get()))
-            .build();
-        break;
-      }
-      case ConnectionStreamIdBlocked: {
-        frameFromTestVectorFile = ConnectionStreamIdBlockedFrame.builder()
-            .maxStreamId(UnsignedLong.valueOf(streamFrameTestVector.maxStreamId().get()))
-            .build();
-        break;
-      }
-      case StreamClose: {
-        frameFromTestVectorFile = StreamCloseFrame.builder()
-            .streamId(UnsignedLong.valueOf(streamFrameTestVector.streamId().get()))
-            .errorCode(ErrorCode.of(streamFrameTestVector.errorCode().get()))
-            .errorMessage(streamFrameTestVector.errorMessage().get())
-            .build();
-        break;
-      }
-      case StreamMoney: {
-        frameFromTestVectorFile = StreamMoneyFrame.builder()
-            .streamId(UnsignedLong.valueOf(streamFrameTestVector.streamId().get()))
-            .shares(UnsignedLong.valueOf(streamFrameTestVector.shares().get()))
-            .build();
-        break;
-      }
-      case StreamMoneyMax: {
-        frameFromTestVectorFile = StreamMoneyMaxFrame.builder()
-            .streamId(UnsignedLong.valueOf(streamFrameTestVector.streamId().get()))
-            .totalReceived(UnsignedLong.valueOf(streamFrameTestVector.totalReceived().get()))
-            .receiveMax(UnsignedLong.valueOf(streamFrameTestVector.receiveMax().get()))
-            .build();
-        break;
-      }
-      case StreamMoneyBlocked: {
-        frameFromTestVectorFile = StreamMoneyBlockedFrame.builder()
-            .streamId(UnsignedLong.valueOf(streamFrameTestVector.streamId().get()))
-            .sendMax(UnsignedLong.valueOf(streamFrameTestVector.sendMax().get()))
-            .totalSent(UnsignedLong.valueOf(streamFrameTestVector.totalSent().get()))
-            .build();
-        break;
-      }
-      case StreamData: {
-        frameFromTestVectorFile = StreamDataFrame.builder()
-            .streamId(UnsignedLong.valueOf(streamFrameTestVector.streamId().get()))
-            .offset(UnsignedLong.valueOf(streamFrameTestVector.offset().get()))
-            .data(Base64.getDecoder().decode(streamFrameTestVector.base64Data().get()))
-            .build();
-        break;
-      }
-      case StreamDataMax: {
-        frameFromTestVectorFile = StreamDataMaxFrame.builder()
-            .streamId(UnsignedLong.valueOf(streamFrameTestVector.streamId().get()))
-            .maxOffset(UnsignedLong.valueOf(streamFrameTestVector.maxOffset().get()))
-            .build();
-        break;
-      }
-      case StreamDataBlocked: {
-        frameFromTestVectorFile = StreamDataBlockedFrame.builder()
-            .streamId(UnsignedLong.valueOf(streamFrameTestVector.streamId().get()))
-            .maxOffset(UnsignedLong.valueOf(streamFrameTestVector.maxOffset().get()))
-            .build();
-        break;
-      }
-      case ConnectionAssetDetails: {
-        frameFromTestVectorFile = ConnectionAssetDetailsFrame.builder().sourceDenomination(
-            Denomination.builder()
-                .assetCode(streamFrameTestVector.sourceAssetCode().get())
-                .assetScale(streamFrameTestVector.sourceAssetScale().get())
-                .build())
-            .build();
-        break;
-      }
-      default: {
-        throw new RuntimeException("Unsupported Frame:" + type);
-      }
-    }
-
-    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-    StreamCodecContextFactory.oer().write(frameFromTestVectorFile, byteArrayOutputStream);
-
-    assertThat(Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()))
-        .isEqualTo(streamFrameTestVector.expectedAsn1OerBytes());
-
-    // Read the bytes, and ensure they match.
-    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
-    final StreamFrame decodedStreamFrame = StreamCodecContextFactory.oer()
-        .read(StreamFrame.class, byteArrayInputStream);
-    assertThat(decodedStreamFrame).isEqualTo(frameFromTestVectorFile);
-  }
-}
diff --git a/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/helpers/StreamFrameFixture.java b/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/helpers/StreamFrameFixture.java
new file mode 100644
index 0000000..0b48e3a
--- /dev/null
+++ b/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/helpers/StreamFrameFixture.java
@@ -0,0 +1,50 @@
+package org.interledger.codecs.stream.frame.helpers;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import org.immutables.value.Value.Immutable;
+
+import java.util.Optional;
+
+/**
+ * A data model object that corresponds to the JSON object defined in `StreamPacketFixtures.json`.
+ */
+@Immutable
+@JsonDeserialize(as = ImmutableStreamFrameFixture.class)
+@JsonSerialize(as = ImmutableStreamFrameFixture.class)
+public interface StreamFrameFixture {
+
+  short type();
+
+  String name();
+
+  Optional<Short> errorCode();
+
+  Optional<String> errorMessage();
+
+  Optional<String> sourceAccount();
+
+  Optional<String> streamId();
+
+  Optional<String> maxStreamId();
+
+  Optional<String> offset();
+
+  Optional<String> maxOffset();
+
+  Optional<String> shares();
+
+  Optional<String> receiveMax();
+
+  Optional<String> totalReceived();
+
+  Optional<String> sendMax();
+
+  Optional<String> totalSent();
+
+  Optional<String> sourceAssetCode();
+
+  Optional<String> sourceAssetScale();
+
+  Optional<String> data();
+}
diff --git a/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/helpers/StreamPacketFixture.java b/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/helpers/StreamPacketFixture.java
new file mode 100644
index 0000000..b17e563
--- /dev/null
+++ b/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/helpers/StreamPacketFixture.java
@@ -0,0 +1,25 @@
+package org.interledger.codecs.stream.frame.helpers;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import org.immutables.value.Value.Immutable;
+
+import java.util.List;
+
+/**
+ * A data model object that corresponds to the JSON object defined in `StreamPacketFixtures.json`.
+ */
+@Immutable
+@JsonDeserialize(as = ImmutableStreamPacketFixture.class)
+@JsonSerialize(as = ImmutableStreamPacketFixture.class)
+public interface StreamPacketFixture {
+
+  String sequence();
+
+  short packetType();
+
+  String amount();
+
+  List<StreamFrameFixture> frames();
+
+}
diff --git a/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/helpers/StreamTestFixture.java b/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/helpers/StreamTestFixture.java
new file mode 100644
index 0000000..b7ac136
--- /dev/null
+++ b/codecs-parent/codecs-stream/src/test/java/org/interledger/codecs/stream/frame/helpers/StreamTestFixture.java
@@ -0,0 +1,32 @@
+package org.interledger.codecs.stream.frame.helpers;
+
+import org.interledger.codecs.stream.frame.helpers.ImmutableStreamTestFixture.Builder;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import org.immutables.value.Value.Immutable;
+
+import java.util.Optional;
+
+/**
+ * A data model object that corresponds to the JSON object defined in `StreamPacketFixtures.json`.
+ */
+@Immutable
+@JsonDeserialize(as = ImmutableStreamTestFixture.class)
+@JsonSerialize(as = ImmutableStreamTestFixture.class)
+public interface StreamTestFixture {
+
+  static Builder builder() {
+    return ImmutableStreamTestFixture.builder();
+  }
+
+  String name();
+
+  StreamPacketFixture packet();
+
+  String buffer();
+
+  @JsonProperty("decode_only")
+  Optional<Boolean> decodeOnly();
+}
diff --git a/codecs-parent/codecs-stream/src/test/resources/StreamPacketFixtures.json b/codecs-parent/codecs-stream/src/test/resources/StreamPacketFixtures.json
new file mode 100644
index 0000000..68e3b58
--- /dev/null
+++ b/codecs-parent/codecs-stream/src/test/resources/StreamPacketFixtures.json
@@ -0,0 +1,831 @@
+[
+  {
+    "name": "sequence:0",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": []
+    },
+    "buffer": "AQwBAAEAAQA="
+  },
+  {
+    "name": "sequence:max_js",
+    "packet": {
+      "sequence": "9007199254740991",
+      "packetType": 12,
+      "amount": "0",
+      "frames": []
+    },
+    "buffer": "AQwHH////////wEAAQA="
+  },
+  {
+    "name": "sequence:max_uint_64",
+    "packet": {
+      "sequence": "18446744073709551615",
+      "packetType": 12,
+      "amount": "0",
+      "frames": []
+    },
+    "buffer": "AQwI//////////8BAAEA"
+  },
+  {
+    "name": "type:prepare",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": []
+    },
+    "buffer": "AQwBAAEAAQA="
+  },
+  {
+    "name": "type:fulfill",
+    "packet": {
+      "sequence": "0",
+      "packetType": 13,
+      "amount": "0",
+      "frames": []
+    },
+    "buffer": "AQ0BAAEAAQA="
+  },
+  {
+    "name": "type:reject",
+    "packet": {
+      "sequence": "0",
+      "packetType": 14,
+      "amount": "0",
+      "frames": []
+    },
+    "buffer": "AQ4BAAEAAQA="
+  },
+  {
+    "name": "amount:0",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": []
+    },
+    "buffer": "AQwBAAEAAQA="
+  },
+  {
+    "name": "amount:max_js",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "9007199254740991",
+      "frames": []
+    },
+    "buffer": "AQwBAAcf////////AQA="
+  },
+  {
+    "name": "amount:max_uint_64",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "18446744073709551615",
+      "frames": []
+    },
+    "buffer": "AQwBAAj//////////wEA"
+  },
+  {
+    "name": "frame:connection_close",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 1,
+          "name": "ConnectionClose",
+          "errorCode": 1,
+          "errorMessage": "fail"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEBBgEEZmFpbA=="
+  },
+  {
+    "name": "frame:connection_new_address",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 2,
+          "name": "ConnectionNewAddress",
+          "sourceAccount": "example.alice"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQECDg1leGFtcGxlLmFsaWNl"
+  },
+  {
+    "name": "frame:connection_asset_details",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 7,
+          "name": "ConnectionAssetDetails",
+          "sourceAssetCode": "ABC",
+          "sourceAssetScale": 255
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEHBQNBQkP/"
+  },
+  {
+    "name": "frame:connection_max_data:0",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 3,
+          "name": "ConnectionMaxData",
+          "maxOffset": "0"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEDAgEA"
+  },
+  {
+    "name": "frame:connection_max_data:max_js",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 3,
+          "name": "ConnectionMaxData",
+          "maxOffset": "9007199254740991"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEDCAcf////////"
+  },
+  {
+    "name": "frame:connection_max_data:max_uint_64",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 3,
+          "name": "ConnectionMaxData",
+          "maxOffset": "18446744073709551615"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEDCQj//////////w=="
+  },
+  {
+    "name": "frame:connection_data_blocked:0",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 4,
+          "name": "ConnectionDataBlocked",
+          "maxOffset": "0"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEEAgEA"
+  },
+  {
+    "name": "frame:connection_data_blocked:max_js",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 4,
+          "name": "ConnectionDataBlocked",
+          "maxOffset": "9007199254740991"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEECAcf////////"
+  },
+  {
+    "name": "frame:connection_data_blocked:max_uint_64",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 4,
+          "name": "ConnectionDataBlocked",
+          "maxOffset": "18446744073709551615"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEECQj//////////w=="
+  },
+  {
+    "name": "frame:connection_max_stream_id:0",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 5,
+          "name": "ConnectionMaxStreamId",
+          "maxStreamId": "0"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEFAgEA"
+  },
+  {
+    "name": "frame:connection_max_stream_id:max_js",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 5,
+          "name": "ConnectionMaxStreamId",
+          "maxStreamId": "9007199254740991"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEFCAcf////////"
+  },
+  {
+    "name": "frame:connection_max_stream_id:max_uint_64",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 5,
+          "name": "ConnectionMaxStreamId",
+          "maxStreamId": "18446744073709551615"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEFCQj//////////w=="
+  },
+  {
+    "name": "frame:connection_stream_id_blocked:0",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 6,
+          "name": "ConnectionStreamIdBlocked",
+          "maxStreamId": "0"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEGAgEA"
+  },
+  {
+    "name": "frame:connection_stream_id_blocked:max_js",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 6,
+          "name": "ConnectionStreamIdBlocked",
+          "maxStreamId": "9007199254740991"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEGCAcf////////"
+  },
+  {
+    "name": "frame:connection_stream_id_blocked:max_uint_64",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 6,
+          "name": "ConnectionStreamIdBlocked",
+          "maxStreamId": "18446744073709551615"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEGCQj//////////w=="
+  },
+  {
+    "name": "frame:stream_close",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 16,
+          "name": "StreamClose",
+          "streamId": "123",
+          "errorCode": 255,
+          "errorMessage": "an error message"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEQFAF7/xBhbiBlcnJvciBtZXNzYWdl"
+  },
+  {
+    "name": "frame:stream_money:0",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 17,
+          "name": "StreamMoney",
+          "streamId": "123",
+          "shares": "0"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQERBAF7AQA="
+  },
+  {
+    "name": "frame:stream_money:max_js",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 17,
+          "name": "StreamMoney",
+          "streamId": "123",
+          "shares": "9007199254740991"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQERCgF7Bx////////8="
+  },
+  {
+    "name": "frame:stream_money:max_uint_64",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 17,
+          "name": "StreamMoney",
+          "streamId": "123",
+          "shares": "18446744073709551615"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQERCwF7CP//////////"
+  },
+  {
+    "name": "frame:stream_max_money:receive_max:0",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 18,
+          "name": "StreamMaxMoney",
+          "streamId": "123",
+          "receiveMax": "0",
+          "totalReceived": "456"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQESBwF7AQACAcg="
+  },
+  {
+    "name": "frame:stream_max_money:receive_max:max_js",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 18,
+          "name": "StreamMaxMoney",
+          "streamId": "123",
+          "receiveMax": "9007199254740991",
+          "totalReceived": "456"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQESDQF7Bx////////8CAcg="
+  },
+  {
+    "name": "frame:stream_max_money:receive_max:max_uint_64",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 18,
+          "name": "StreamMaxMoney",
+          "streamId": "123",
+          "receiveMax": "18446744073709551615",
+          "totalReceived": "456"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQESDgF7CP//////////AgHI"
+  },
+  {
+    "name": "frame:stream_max_money:total_received:0",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 18,
+          "name": "StreamMaxMoney",
+          "streamId": "123",
+          "receiveMax": "456",
+          "totalReceived": "0"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQESBwF7AgHIAQA="
+  },
+  {
+    "name": "frame:stream_max_money:total_received:max_js",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 18,
+          "name": "StreamMaxMoney",
+          "streamId": "123",
+          "receiveMax": "456",
+          "totalReceived": "9007199254740991"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQESDQF7AgHIBx////////8="
+  },
+  {
+    "name": "frame:stream_max_money:total_received:max_uint_64",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 18,
+          "name": "StreamMaxMoney",
+          "streamId": "123",
+          "receiveMax": "456",
+          "totalReceived": "18446744073709551615"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQESDgF7AgHICP//////////"
+  },
+  {
+    "name": "frame:stream_money_blocked:send_max:0",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 19,
+          "name": "StreamMoneyBlocked",
+          "streamId": "123",
+          "sendMax": "0",
+          "totalSent": "456"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQETBwF7AQACAcg="
+  },
+  {
+    "name": "frame:stream_money_blocked:send_max:max_js",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 19,
+          "name": "StreamMoneyBlocked",
+          "streamId": "123",
+          "sendMax": "9007199254740991",
+          "totalSent": "456"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQETDQF7Bx////////8CAcg="
+  },
+  {
+    "name": "frame:stream_money_blocked:send_max:max_uint_64",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 19,
+          "name": "StreamMoneyBlocked",
+          "streamId": "123",
+          "sendMax": "18446744073709551615",
+          "totalSent": "456"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQETDgF7CP//////////AgHI"
+  },
+  {
+    "name": "frame:stream_money_blocked:total_sent:0",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 19,
+          "name": "StreamMoneyBlocked",
+          "streamId": "123",
+          "sendMax": "456",
+          "totalSent": "0"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQETBwF7AgHIAQA="
+  },
+  {
+    "name": "frame:stream_money_blocked:total_sent:max_js",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 19,
+          "name": "StreamMoneyBlocked",
+          "streamId": "123",
+          "sendMax": "456",
+          "totalSent": "9007199254740991"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQETDQF7AgHIBx////////8="
+  },
+  {
+    "name": "frame:stream_money_blocked:total_sent:max_uint_64",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 19,
+          "name": "StreamMoneyBlocked",
+          "streamId": "123",
+          "sendMax": "456",
+          "totalSent": "18446744073709551615"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQETDgF7AgHICP//////////"
+  },
+  {
+    "name": "frame:stream_data",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 20,
+          "name": "StreamData",
+          "streamId": "123",
+          "offset": "456",
+          "data": "Zm9vYmFy"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEUDAF7AgHIBmZvb2Jhcg=="
+  },
+  {
+    "name": "frame:stream_data:offset:0",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 20,
+          "name": "StreamData",
+          "streamId": "123",
+          "offset": "0",
+          "data": ""
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEUBQF7AQAA"
+  },
+  {
+    "name": "frame:stream_data:offset:max_js",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 20,
+          "name": "StreamData",
+          "streamId": "123",
+          "offset": "9007199254740991",
+          "data": ""
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEUCwF7Bx////////8A"
+  },
+  {
+    "name": "frame:stream_data:offset:max_uint_64",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 20,
+          "name": "StreamData",
+          "streamId": "123",
+          "offset": "18446744073709551615",
+          "data": ""
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEUDAF7CP//////////AA=="
+  },
+  {
+    "name": "frame:stream_max_data:offset:0",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 21,
+          "name": "StreamMaxData",
+          "streamId": "123",
+          "maxOffset": "0"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEVBAF7AQA="
+  },
+  {
+    "name": "frame:stream_max_data:offset:max_js",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 21,
+          "name": "StreamMaxData",
+          "streamId": "123",
+          "maxOffset": "9007199254740991"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEVCgF7Bx////////8="
+  },
+  {
+    "name": "frame:stream_max_data:offset:max_uint_64",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 21,
+          "name": "StreamMaxData",
+          "streamId": "123",
+          "maxOffset": "18446744073709551615"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEVCwF7CP//////////"
+  },
+  {
+    "name": "frame:stream_data_blocked:offset:0",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 22,
+          "name": "StreamDataBlocked",
+          "streamId": "123",
+          "maxOffset": "0"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEWBAF7AQA="
+  },
+  {
+    "name": "frame:stream_data_blocked:offset:max_js",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 22,
+          "name": "StreamDataBlocked",
+          "streamId": "123",
+          "maxOffset": "9007199254740991"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEWCgF7Bx////////8="
+  },
+  {
+    "name": "frame:stream_data_blocked:offset:max_uint_64",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 22,
+          "name": "StreamDataBlocked",
+          "streamId": "123",
+          "maxOffset": "18446744073709551615"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQEWCwF7CP//////////"
+  },
+  {
+    "name": "frame:stream_max_money:receive_max:too_big",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 18,
+          "name": "StreamMaxMoney",
+          "streamId": "123",
+          "receiveMax": "18446744073709551615",
+          "totalReceived": "456"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQESDwF7CQEAAAAAAAAAAAIByA==",
+    "decode_only": true
+  },
+  {
+    "name": "frame:stream_money_blocked:send_max:too_big",
+    "packet": {
+      "sequence": "0",
+      "packetType": 12,
+      "amount": "0",
+      "frames": [
+        {
+          "type": 19,
+          "name": "StreamMoneyBlocked",
+          "streamId": "123",
+          "sendMax": "18446744073709551615",
+          "totalSent": "456"
+        }
+      ]
+    },
+    "buffer": "AQwBAAEAAQETDwF7CQEAAAAAAAAAAAIByA==",
+    "decode_only": true
+  }
+]
diff --git a/codecs-parent/codecs-stream/src/test/resources/ValidStreamFrameVectorsTest.json b/codecs-parent/codecs-stream/src/test/resources/ValidStreamFrameVectorsTest.json
deleted file mode 100644
index 33c8067..0000000
--- a/codecs-parent/codecs-stream/src/test/resources/ValidStreamFrameVectorsTest.json
+++ /dev/null
@@ -1,85 +0,0 @@
-[
-  {
-    "type": "ConnectionClose",
-    "errorCode": "1",
-    "errorMessage": "I have to tell you something about the future!",
-    "expectedAsn1OerBytes": "ATABLkkgaGF2ZSB0byB0ZWxsIHlvdSBzb21ldGhpbmcgYWJvdXQgdGhlIGZ1dHVyZSE="
-  },
-  {
-    "type": "ConnectionNewAddress",
-    "sourceAddress": "test.example.marty",
-    "expectedAsn1OerBytes": "AhMSdGVzdC5leGFtcGxlLm1hcnR5"
-  },
-  {
-    "type": "ConnectionDataMax",
-    "maxOffset": "2",
-    "expectedAsn1OerBytes": "AwIBAg=="
-  },
-  {
-    "type": "ConnectionDataBlocked",
-    "maxOffset": "3",
-    "expectedAsn1OerBytes": "BAIBAw=="
-  },
-  {
-    "type": "ConnectionMaxStreamId",
-    "maxStreamId": "4",
-    "expectedAsn1OerBytes": "BQIBBA=="
-  },
-  {
-    "type": "ConnectionStreamIdBlocked",
-    "maxStreamId": "5",
-    "expectedAsn1OerBytes": "BgIBBQ=="
-  },
-  {
-    "type": "StreamClose",
-    "streamId": 6,
-    "errorCode": 7,
-    "errorMessage": "I have to tell you something about the future!",
-    "expectedAsn1OerBytes": "EDIBBgcuSSBoYXZlIHRvIHRlbGwgeW91IHNvbWV0aGluZyBhYm91dCB0aGUgZnV0dXJlIQ=="
-  },
-  {
-    "type": "StreamMoney",
-    "streamId": 8,
-    "shares": 9,
-    "expectedAsn1OerBytes": "EQQBCAEJ"
-  },
-  {
-    "type": "StreamMoneyMax",
-    "streamId": 10,
-    "receiveMax": 11,
-    "totalReceived": 12,
-    "expectedAsn1OerBytes": "EgYBCgELAQw="
-  },
-  {
-    "type": "StreamMoneyBlocked",
-    "streamId": 13,
-    "sendMax": 14,
-    "totalSent": 15,
-    "expectedAsn1OerBytes": "EwYBDQEOAQ8="
-  },
-  {
-    "type": "StreamData",
-    "streamId": 16,
-    "offset": 17,
-    "base64Data": "foo",
-    "expectedAsn1OerBytes": "FAcBEAERAn6K"
-  },
-  {
-    "type": "StreamDataMax",
-    "streamId": 18,
-    "maxOffset": 19,
-    "expectedAsn1OerBytes": "FQQBEgET"
-  },
-  {
-    "type": "StreamDataBlocked",
-    "streamId": 20,
-    "maxOffset": 21,
-    "expectedAsn1OerBytes": "FgQBFAEV"
-  },
-  {
-    "type": "ConnectionAssetDetails",
-    "sourceAssetCode": "USD",
-    "sourceAssetScale": 22,
-    "expectedAsn1OerBytes": "BwUDVVNEFg=="
-  }
-]
diff --git a/stream-parent/stream-core/src/test/java/org/interledger/stream/frames/ErrorCodeTest.java b/stream-parent/stream-core/src/test/java/org/interledger/stream/frames/ErrorCodeTest.java
deleted file mode 100644
index 4ff0f6b..0000000
--- a/stream-parent/stream-core/src/test/java/org/interledger/stream/frames/ErrorCodeTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package org.interledger.stream.frames;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.interledger.stream.frames.ErrorCodeConstants.APPLICATION_ERROR;
-import static org.interledger.stream.frames.ErrorCodeConstants.ENDPOINT_BUSY;
-import static org.interledger.stream.frames.ErrorCodeConstants.FLOW_CONTROL_ERROR;
-import static org.interledger.stream.frames.ErrorCodeConstants.FRAME_FORMAT_ERROR;
-import static org.interledger.stream.frames.ErrorCodeConstants.INTERNAL_ERROR;
-import static org.interledger.stream.frames.ErrorCodeConstants.NO_ERROR;
-import static org.interledger.stream.frames.ErrorCodeConstants.PROTOCOL_VIOLATION;
-import static org.interledger.stream.frames.ErrorCodeConstants.STREAM_ID_ERROR;
-import static org.interledger.stream.frames.ErrorCodeConstants.STREAM_STATE_ERROR;
-
-import com.google.common.collect.Lists;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-
-public class ErrorCodeTest {
-
-  private final List<Short> expected = Lists.newArrayList(
-      NO_ERROR,
-      INTERNAL_ERROR,
-      ENDPOINT_BUSY,
-      FLOW_CONTROL_ERROR,
-      STREAM_ID_ERROR,
-      STREAM_STATE_ERROR,
-      FRAME_FORMAT_ERROR,
-      PROTOCOL_VIOLATION,
-      APPLICATION_ERROR
-  );
-
-  @Rule
-  public ExpectedException expectedException = ExpectedException.none();
-
-  @Test
-  public void of() {
-    assertThat(expected.stream().map(ErrorCode::of).collect(Collectors.toList()))
-        .isEqualTo(Arrays.asList(ErrorCode.values()));
-  }
-
-  @Test
-  public void ofThrows() {
-    expectedException.expect(RuntimeException.class);
-    ErrorCode.of((short) 0x17);
-  }
-
-  @Test
-  public void codeAndDescription() {
-    final String noErrorDesc = "The stream or connection closed normally.";
-    final String internalErrorDesc = "The endpoint encountered an unexpected error.";
-    final String endpointBusyDesc = "The endpoint is temporarily overloaded and unable to process the packet.";
-    final String flowControlErrorDesc = "The other endpoint exceeded the flow control limits advertised.";
-    final String streamIdErrorDesc = "The other endpoint opened more streams than allowed.";
-    final String streamStateErrorDesc = "The other endpoint sent frames for a stream that is already closed.";
-    final String frameFormatErrorDesc = "The other endpoint sent a frame with invalid syntax.";
-    final String protocolViolationDesc = "The other endpoint sent invalid data or otherwise violated the protocol.";
-    final String applicationErrorDesc = "The application using STREAM closed the stream or connection with an error.";
-
-    assertThat(ErrorCode.NoError.getCode()).isEqualTo((short) 0x01);
-    assertThat(ErrorCode.NoError.getDescription()).isEqualTo(noErrorDesc);
-    assertThat(ErrorCode.InternalError.getCode()).isEqualTo((short) 0x02);
-    assertThat(ErrorCode.InternalError.getDescription()).isEqualTo(internalErrorDesc);
-    assertThat(ErrorCode.EndpointBusy.getCode()).isEqualTo((short) 0x03);
-    assertThat(ErrorCode.EndpointBusy.getDescription()).isEqualTo(endpointBusyDesc);
-    assertThat(ErrorCode.FlowControlError.getCode()).isEqualTo((short) 0x04);
-    assertThat(ErrorCode.FlowControlError.getDescription()).isEqualTo(flowControlErrorDesc);
-    assertThat(ErrorCode.StreamIdError.getCode()).isEqualTo((short) 0x05);
-    assertThat(ErrorCode.StreamIdError.getDescription()).isEqualTo(streamIdErrorDesc);
-    assertThat(ErrorCode.StreamStateError.getCode()).isEqualTo((short) 0x06);
-    assertThat(ErrorCode.StreamStateError.getDescription()).isEqualTo(streamStateErrorDesc);
-    assertThat(ErrorCode.FrameFormatError.getCode()).isEqualTo((short) 0x07);
-    assertThat(ErrorCode.FrameFormatError.getDescription()).isEqualTo(frameFormatErrorDesc);
-    assertThat(ErrorCode.ProtocolViolation.getCode()).isEqualTo((short) 0x08);
-    assertThat(ErrorCode.ProtocolViolation.getDescription()).isEqualTo(protocolViolationDesc);
-    assertThat(ErrorCode.ApplicationError.getCode()).isEqualTo((short) 0x09);
-    assertThat(ErrorCode.ApplicationError.getDescription()).isEqualTo(applicationErrorDesc);
-  }
-
-  @Test
-  public void testToStringFormatting() {
-    String errorCodeFormatting = "ErrorCode[code=1, description='The stream or connection closed normally.']";
-    assertThat(ErrorCode.NoError.toString()).isEqualTo(errorCodeFormatting);
-  }
-
-}
diff --git a/stream-parent/stream-core/src/test/java/org/interledger/stream/frames/ErrorCodesTest.java b/stream-parent/stream-core/src/test/java/org/interledger/stream/frames/ErrorCodesTest.java
new file mode 100644
index 0000000..f7810db
--- /dev/null
+++ b/stream-parent/stream-core/src/test/java/org/interledger/stream/frames/ErrorCodesTest.java
@@ -0,0 +1,95 @@
+package org.interledger.stream.frames;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.interledger.stream.frames.ErrorCodeConstants.APPLICATION_ERROR;
+import static org.interledger.stream.frames.ErrorCodeConstants.ENDPOINT_BUSY;
+import static org.interledger.stream.frames.ErrorCodeConstants.FLOW_CONTROL_ERROR;
+import static org.interledger.stream.frames.ErrorCodeConstants.FRAME_FORMAT_ERROR;
+import static org.interledger.stream.frames.ErrorCodeConstants.INTERNAL_ERROR;
+import static org.interledger.stream.frames.ErrorCodeConstants.NO_ERROR;
+import static org.interledger.stream.frames.ErrorCodeConstants.PROTOCOL_VIOLATION;
+import static org.interledger.stream.frames.ErrorCodeConstants.STREAM_ID_ERROR;
+import static org.interledger.stream.frames.ErrorCodeConstants.STREAM_STATE_ERROR;
+import static org.interledger.stream.frames.ErrorCodeConstants.UNSUPPORTED_ERROR;
+
+import com.google.common.collect.Lists;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class ErrorCodesTest {
+
+  private final List<Short> expected = Lists.newArrayList(
+      UNSUPPORTED_ERROR,
+      NO_ERROR,
+      INTERNAL_ERROR,
+      ENDPOINT_BUSY,
+      FLOW_CONTROL_ERROR,
+      STREAM_ID_ERROR,
+      STREAM_STATE_ERROR,
+      FRAME_FORMAT_ERROR,
+      PROTOCOL_VIOLATION,
+      APPLICATION_ERROR
+  );
+
+  @Test
+  public void of() {
+    assertThat(expected.stream().map(ErrorCodes::of).collect(Collectors.toList()))
+        .isEqualTo(Arrays.asList(ErrorCodes.values()).stream()
+            .map(ErrorCode.builder()::from)
+            .map(ErrorCodeBuilder::build)
+            .collect(Collectors.toList())
+        );
+  }
+
+  @Test
+  public void codeAnddescription() {
+    final String unknownErrorDesc = "An unsupported error code was supplied.";
+    final String noErrorDesc = "The stream or connection closed normally.";
+    final String internalErrorDesc = "The endpoint encountered an unexpected error.";
+    final String endpointBusyDesc = "The endpoint is temporarily overloaded and unable to process the packet.";
+    final String flowControlErrorDesc = "The other endpoint exceeded the flow control limits advertised.";
+    final String streamIdErrorDesc = "The other endpoint opened more streams than allowed.";
+    final String streamStateErrorDesc = "The other endpoint sent frames for a stream that is already closed.";
+    final String frameFormatErrorDesc = "The other endpoint sent a frame with invalid syntax.";
+    final String protocolViolationDesc = "The other endpoint sent invalid data or otherwise violated the protocol.";
+    final String applicationErrorDesc = "The application using STREAM closed the stream or connection with an error.";
+
+    assertThat(ErrorCodes.UnsupportedError.description()).isEqualTo(unknownErrorDesc);
+    assertThat(ErrorCodes.UnsupportedError.code()).isEqualTo((short) 0);
+    assertThat(ErrorCodes.NoError.code()).isEqualTo((short) 0x01);
+    assertThat(ErrorCodes.NoError.description()).isEqualTo(noErrorDesc);
+    assertThat(ErrorCodes.InternalError.code()).isEqualTo((short) 0x02);
+    assertThat(ErrorCodes.InternalError.description()).isEqualTo(internalErrorDesc);
+    assertThat(ErrorCodes.EndpointBusy.code()).isEqualTo((short) 0x03);
+    assertThat(ErrorCodes.EndpointBusy.description()).isEqualTo(endpointBusyDesc);
+    assertThat(ErrorCodes.FlowControlError.code()).isEqualTo((short) 0x04);
+    assertThat(ErrorCodes.FlowControlError.description()).isEqualTo(flowControlErrorDesc);
+    assertThat(ErrorCodes.StreamIdError.code()).isEqualTo((short) 0x05);
+    assertThat(ErrorCodes.StreamIdError.description()).isEqualTo(streamIdErrorDesc);
+    assertThat(ErrorCodes.StreamStateError.code()).isEqualTo((short) 0x06);
+    assertThat(ErrorCodes.StreamStateError.description()).isEqualTo(streamStateErrorDesc);
+    assertThat(ErrorCodes.FrameFormatError.code()).isEqualTo((short) 0x07);
+    assertThat(ErrorCodes.FrameFormatError.description()).isEqualTo(frameFormatErrorDesc);
+    assertThat(ErrorCodes.ProtocolViolation.code()).isEqualTo((short) 0x08);
+    assertThat(ErrorCodes.ProtocolViolation.description()).isEqualTo(protocolViolationDesc);
+    assertThat(ErrorCodes.ApplicationError.code()).isEqualTo((short) 0x09);
+    assertThat(ErrorCodes.ApplicationError.description()).isEqualTo(applicationErrorDesc);
+  }
+
+  @Test
+  public void testToStringFormatting() {
+    String errorCodeFormatting = "ErrorCodes[code=1, description='The stream or connection closed normally.']";
+    assertThat(ErrorCodes.NoError.toString()).isEqualTo(errorCodeFormatting);
+  }
+
+  @Test
+  public void testUnsupportedErrorCodes() {
+    for (short i = 10; i <= 255; i++) {
+      assertThat(ErrorCodes.of(i).code()).isEqualTo(i);
+      assertThat(ErrorCodes.of(i).description()).isEqualTo(ErrorCodes.UnsupportedError.description());
+    }
+  }
+}
diff --git a/stream-parent/stream-core/src/test/java/org/interledger/stream/frames/StreamCloseFrameTest.java b/stream-parent/stream-core/src/test/java/org/interledger/stream/frames/StreamCloseFrameTest.java
index eddf6d2..01d0cdf 100644
--- a/stream-parent/stream-core/src/test/java/org/interledger/stream/frames/StreamCloseFrameTest.java
+++ b/stream-parent/stream-core/src/test/java/org/interledger/stream/frames/StreamCloseFrameTest.java
@@ -12,7 +12,7 @@ public class StreamCloseFrameTest {
   public void emptyMessage() {
     StreamCloseFrame frame = StreamCloseFrame.builder()
         .streamId(UnsignedLong.ZERO)
-        .errorCode(ErrorCode.NoError)
+        .errorCode(ErrorCodes.NoError)
         .build();
     assertThat(frame.errorMessage()).isEmpty();
     assertThat(spy(StreamCloseFrame.class).errorMessage()).isEmpty();
diff --git a/stream-parent/stream-core/src/test/java/org/interledger/stream/frames/StreamFrameTypeTest.java b/stream-parent/stream-core/src/test/java/org/interledger/stream/frames/StreamFrameTypeTest.java
index 6c3e4b5..6732673 100644
--- a/stream-parent/stream-core/src/test/java/org/interledger/stream/frames/StreamFrameTypeTest.java
+++ b/stream-parent/stream-core/src/test/java/org/interledger/stream/frames/StreamFrameTypeTest.java
@@ -109,7 +109,7 @@ public class StreamFrameTypeTest {
   public void connectionCloseFrame() {
     ConnectionCloseFrame frame = ConnectionCloseFrame.builder()
         .errorMessage("too many cooks!")
-        .errorCode(ErrorCode.ApplicationError)
+        .errorCode(ErrorCodes.ApplicationError)
         .build();
     assertThat(frame.streamFrameType()).isEqualTo(StreamFrameType.ConnectionClose);
     // make sure interface default method is exercised
@@ -174,7 +174,7 @@ public class StreamFrameTypeTest {
     StreamCloseFrame frame = StreamCloseFrame.builder()
         .streamId(UnsignedLong.ZERO)
         .errorMessage("i'm all out of love")
-        .errorCode(ErrorCode.ApplicationError)
+        .errorCode(ErrorCodes.ApplicationError)
         .build();
     assertThat(frame.streamFrameType()).isEqualTo(StreamFrameType.StreamClose);
     // make sure interface default method is exercised