src/arbitraries
fast-check generators for MLLP, at @cosyte/mllp/arbitraries: payloads, canonical frames,
canonical byte streams split into chunks, messy streams a lenient reader accepts, and hostile
streams. We use them to test this library, and you can use them to test code you build on it.
fast-check is an optional peer dependency: install it to use this entry point. The main entry
point, @cosyte/mllp, never loads it. This entry point imports nothing from the library at run
time, only its types.
Every generated value is synthetic and random. Each one keeps a copy of itself, which fast-check clones and prints, so code that changes the bytes it is given cannot change the case fast-check reports.
Example
import fc from "fast-check";
import { FrameReader, encodeFrame } from "@cosyte/mllp";
import { payload } from "@cosyte/mllp/arbitraries";
fc.assert(
fc.property(payload(), (p) => {
let got: Buffer | undefined;
new FrameReader({ onFrame: (b) => (got = b) }).push(encodeFrame(p));
return got?.equals(p) === true;
}),
);
Interfaces
ChunkOptions
Options for chunked.
Extended by
Properties
maxCuts?
readonlyoptionalmaxCuts?:number
The most places the bytes are cut. Default 16. Two cuts in one place leave an empty chunk.
GeneratedFrame
A canonical frame and the payload it carries.
Properties
bytes
readonlybytes:Buffer
The frame as it goes on the wire: VT, the payload, FS, CR.
payload
readonlypayload:Buffer
The payload: what a reader must deliver for these bytes.
GeneratedStream
A canonical byte stream: frames back to back, as chunks, with the payloads it carries.
Extended by
Properties
bytes
readonlybytes:Buffer
The whole stream: each payload's canonical frame, back to back.
chunks
readonlychunks: readonlyBuffer<ArrayBufferLike>[]
The same bytes split at random places, the reads a socket may deliver.
payloads
readonlypayloads: readonlyBuffer<ArrayBufferLike>[]
The payloads in order: what a reader must deliver for this stream.
HostileInputOptions
Options for hostileInput.
Properties
maxCuts?
readonlyoptionalmaxCuts?:number
The most places the stream is cut into chunks. Default 16.
maxLength?
readonlyoptionalmaxLength?:number
The most bytes in the stream. Default 512.
MessyStream
A messy stream, with what a reader with the matching tolerances must deliver for it.
Extends
Properties
bytes
readonlybytes:Buffer
The whole stream: each payload's canonical frame, back to back.
Inherited from
chunks
readonlychunks: readonlyBuffer<ArrayBufferLike>[]
The same bytes split at random places, the reads a socket may deliver.
Inherited from
payloads
readonlypayloads: readonlyBuffer<ArrayBufferLike>[]
The payloads in order: what a reader must deliver for this stream.
Inherited from
warnings
readonlywarnings: readonly (readonlyWarningCode[])[]
The warning codes each delivered frame must carry, in the order they are raised, one list per payload. A frame with no quirk has an empty list.
MessyStreamOptions
Options for messyStream: the payloads, the frames and the cuts as for a canonical stream,
and the tolerances of the reader the stream is meant for. A quirk whose tolerance is false is
never generated, so pass the same flags your FrameReader or server has. Each defaults to true.
Extends
Properties
allowFsOnly?
readonlyoptionalallowFsOnly?:boolean
Generate FS with no CR after it.
allowLeadingWhitespace?
readonlyoptionalallowLeadingWhitespace?:boolean
Generate spaces, tabs, line feeds and carriage returns before a frame.
allowLfAfterFs?
readonlyoptionalallowLfAfterFs?:boolean
Generate FS then LF in place of FS then CR.
allowMissingLeadingVt?
readonlyoptionalallowMissingLeadingVt?:boolean
Generate a frame that opens with no VT.
content?
readonlyoptionalcontent?:PayloadContent
What the payload holds.
"bytes"(the default): any byte exceptVT(0x0B) andFS(0x1C), the two bytes the encoder refuses.CRand the other control bytes are in."text": printable ASCII with carriage returns, the shape of HL7 v2 text."hl7": a synthetic HL7 v2 message: anMSHsegment with a generated control id in MSH-10, then up to four more segments, each ended by a carriage return.minLengthandmaxLengthdo not apply.
Inherited from
maxCuts?
readonlyoptionalmaxCuts?:number
The most places the bytes are cut. Default 16. Two cuts in one place leave an empty chunk.
Inherited from
maxFrames?
readonlyoptionalmaxFrames?:number
The most frames in the stream. Default 5.
Inherited from
maxLength?
readonlyoptionalmaxLength?:number
The most bytes in a "bytes" or "text" payload. Default 256.
Inherited from
minFrames?
readonlyoptionalminFrames?:number
The fewest frames in the stream. Default 1.
Inherited from
minLength?
readonlyoptionalminLength?:number
The fewest bytes in a "bytes" or "text" payload. Default 1.
Inherited from
PayloadOptions
Options for payload, and for the payloads inside frames and streams.
Extended by
Properties
content?
readonlyoptionalcontent?:PayloadContent
What the payload holds.
"bytes"(the default): any byte exceptVT(0x0B) andFS(0x1C), the two bytes the encoder refuses.CRand the other control bytes are in."text": printable ASCII with carriage returns, the shape of HL7 v2 text."hl7": a synthetic HL7 v2 message: anMSHsegment with a generated control id in MSH-10, then up to four more segments, each ended by a carriage return.minLengthandmaxLengthdo not apply.
maxLength?
readonlyoptionalmaxLength?:number
The most bytes in a "bytes" or "text" payload. Default 256.
minLength?
readonlyoptionalminLength?:number
The fewest bytes in a "bytes" or "text" payload. Default 1.
StreamOptions
Options for stream.
Extends
Extended by
Properties
content?
readonlyoptionalcontent?:PayloadContent
What the payload holds.
"bytes"(the default): any byte exceptVT(0x0B) andFS(0x1C), the two bytes the encoder refuses.CRand the other control bytes are in."text": printable ASCII with carriage returns, the shape of HL7 v2 text."hl7": a synthetic HL7 v2 message: anMSHsegment with a generated control id in MSH-10, then up to four more segments, each ended by a carriage return.minLengthandmaxLengthdo not apply.
Inherited from
maxCuts?
readonlyoptionalmaxCuts?:number
The most places the bytes are cut. Default 16. Two cuts in one place leave an empty chunk.
Inherited from
maxFrames?
readonlyoptionalmaxFrames?:number
The most frames in the stream. Default 5.
maxLength?
readonlyoptionalmaxLength?:number
The most bytes in a "bytes" or "text" payload. Default 256.
Inherited from
minFrames?
readonlyoptionalminFrames?:number
The fewest frames in the stream. Default 1.
minLength?
readonlyoptionalminLength?:number
The fewest bytes in a "bytes" or "text" payload. Default 1.
Inherited from
Type Aliases
PayloadContent
PayloadContent =
"bytes"|"text"|"hl7"
What a generated payload holds.
Functions
chunked()
chunked(
bytes,options?):Arbitrary<Buffer<ArrayBufferLike>[]>
The given bytes split into chunks at random places, the way a socket may deliver them: a frame across many reads, many frames in one read, and now and then an empty read. Concatenated, the chunks are always the bytes given. A case shrinks toward one chunk.
Parameters
bytes
Buffer
The bytes to split.
options?
ChunkOptions = {}
How many cuts at most.
Returns
Arbitrary<Buffer<ArrayBufferLike>[]>
An arbitrary of chunk lists.
Example
import fc from "fast-check";
import { chunked } from "@cosyte/mllp/arbitraries";
const bytes = Buffer.from("ABCDEF");
fc.assert(fc.property(chunked(bytes), (chunks) => Buffer.concat(chunks).equals(bytes)));
frame()
frame(
options?):Arbitrary<GeneratedFrame>
A canonical MLLP frame, VT + payload + FS + CR, with the payload it carries.
The bytes are written from the MLLP specification, not by encodeFrame, so they are an
independent check on the encoder: encodeFrame(f.payload) equals f.bytes.
Parameters
options?
PayloadOptions = {}
The payload's options, as for payload.
Returns
Arbitrary<GeneratedFrame>
An arbitrary of frames.
Example
import fc from "fast-check";
import { encodeFrame } from "@cosyte/mllp";
import { frame } from "@cosyte/mllp/arbitraries";
fc.assert(fc.property(frame(), (f) => encodeFrame(f.payload).equals(f.bytes)));
hostileInput()
hostileInput(
options?):Arbitrary<Buffer<ArrayBufferLike>[]>
A hostile MLLP byte stream as the chunks a socket delivers: random bytes, bytes thick with
VT, FS, CR, LF and whitespace, and messy streams with bytes inserted, deleted, replaced,
repeated and cut off. A reader may refuse one, but it must not hang, crash or throw anything it
does not document.
Parameters
options?
HostileInputOptions = {}
The most bytes, and the most cuts.
Returns
Arbitrary<Buffer<ArrayBufferLike>[]>
An arbitrary of chunk lists.
Example
import fc from "fast-check";
import { FrameReader, MllpFramingError } from "@cosyte/mllp";
import { hostileInput } from "@cosyte/mllp/arbitraries";
fc.assert(
fc.property(hostileInput(), (chunks) => {
const reader = new FrameReader({ onFrame: () => {} });
try {
for (const chunk of chunks) reader.push(chunk);
} catch (err) {
if (!(err instanceof MllpFramingError)) throw err;
}
}),
);
messyStream()
messyStream(
options?):Arbitrary<MessyStream>
A messy MLLP byte stream a lenient reader accepts, split into chunks at random places, with the payloads it must deliver and the warnings each must carry.
Each frame may open with spaces, tabs, line feeds or carriage returns before its VT, or with no
VT at all; carry an empty payload; carry bytes before a second VT that the reader discards
(MLLP_TRAILING_BYTES, the payload delivered is what follows that VT); and end with FS and
LF, with FS and the next frame's VT, or with FS and a stray byte the reader drops. Give it
the tolerances your reader has, and it generates only what that reader accepts.
Parameters
options?
MessyStreamOptions = {}
The payloads, frames, cuts and tolerances.
Returns
Arbitrary<MessyStream>
An arbitrary of messy streams.
Example
import fc from "fast-check";
import { FrameReader } from "@cosyte/mllp";
import { messyStream } from "@cosyte/mllp/arbitraries";
const tolerances = { allowFsOnly: true, allowLfAfterFs: true, allowLeadingWhitespace: true };
fc.assert(
fc.property(messyStream({ ...tolerances, allowMissingLeadingVt: false }), (s) => {
const got: Buffer[] = [];
const reader = new FrameReader({ ...tolerances, onFrame: (p) => got.push(p) });
for (const chunk of s.chunks) reader.push(chunk);
return got.length === s.payloads.length;
}),
);
payload()
payload(
options?):Arbitrary<Buffer<ArrayBufferLike>>
A payload the encoder accepts: a Buffer with no VT (0x0B) and no FS (0x1C) in it.
Framed with encodeFrame and read back with a FrameReader, every one comes back byte for
byte. With the default minLength of 1 it also comes back with no warning; an empty payload
comes back with MLLP_EMPTY_PAYLOAD.
Parameters
options?
PayloadOptions = {}
What the payload holds and how long it is.
Returns
Arbitrary<Buffer<ArrayBufferLike>>
An arbitrary of payload buffers.
Example
import fc from "fast-check";
import { encodeFrame } from "@cosyte/mllp";
import { payload } from "@cosyte/mllp/arbitraries";
fc.assert(fc.property(payload(), (p) => encodeFrame(p).length === p.length + 3));
stream()
stream(
options?):Arbitrary<GeneratedStream>
A canonical MLLP byte stream: one to five frames back to back, split into chunks at random places, with the payloads a reader must deliver from it, in order and with no warning.
Parameters
options?
StreamOptions = {}
The payloads' options, how many frames, and how many cuts at most.
Returns
Arbitrary<GeneratedStream>
An arbitrary of streams.
Example
import fc from "fast-check";
import { FrameReader } from "@cosyte/mllp";
import { stream } from "@cosyte/mllp/arbitraries";
fc.assert(
fc.property(stream(), (s) => {
const got: Buffer[] = [];
const reader = new FrameReader({ onFrame: (p) => got.push(p) });
for (const chunk of s.chunks) reader.push(chunk);
return got.length === s.payloads.length;
}),
);