Rupa.Gen (rupa v0.1.0)

Copy Markdown View Source

StreamData generators built from a schema, for the round-trip property.

Add stream_data to your own deps to use this — Rupa lists it as optional, so it is not forced on anyone who only wants to decode.

property "encode and decode are inverses" do
  codec = Rupa.compile!(MyApp.Schemas.user())

  check all value <- Rupa.Gen.stream(codec) do
    assert {:ok, wire} = Rupa.encode(codec, value)
    assert {:ok, ^value} = Rupa.decode(codec, wire)
  end
end

What it generates is decoded values — what Rupa.decode/3 returns and Rupa.encode/3 takes — because that is the side the property is stated on. A field carrying default: is always generated, even though it is optional on the wire: leave it out and decoding fills it in, and the value that comes back is not the one you started with.

What it refuses

A generator that quietly skips half your schema is worse than one that will not start, so these raise with the path rather than being ignored:

  • pattern: — generating a string from a PCRE is a project of its own
  • multiple_of: on a float, or one that is not a whole number on an integer — the multiples of 0.1 are not representable, so the values it produced would fail the check they were generated to satisfy
  • a bound that nothing satisfies, such as T.integer(gt: 1, lt: 2) or a float interval with no double in it
  • a recursion with no case that stops, such as %{child: T.ref(:root)}, which has no finite value at all

Untagged unions

An untagged union generates from one of its variants at random, which round-trips only if the variants are disjoint — if two of them accept the same value, decoding picks the first and the property will tell you so. That is the cost tag: :none makes you opt into, and Rupa.T.tagged/3 is the version with nothing to be ambiguous about.

Summary

Functions

A generator of decoded values for a codec, a generated module, or a schema.

Functions

stream(module)

@spec stream(term()) :: StreamData.t(term())

A generator of decoded values for a codec, a generated module, or a schema.

iex> [value] = %{n: Rupa.T.integer(gte: 1, lte: 3)} |> Rupa.Gen.stream() |> Enum.take(1)
iex> value.n in 1..3
true