[![CI](https://github.com/IgnacioGoldchluk/more_stream_data/actions/workflows/ci.yml/badge.svg)](https://github.com/IgnacioGoldchluk/more_stream_data/actions/workflows/ci.yml)
[![License](https://img.shields.io/hexpm/l/more_stream_data
)](https://github.com/IgnacioGoldchluk/more_stream_data/blob/main/LICENSE.md)
[![Version](https://img.shields.io/hexpm/v/more_stream_data.svg)](https://hex.pm/packages/more_stream_data)
[![Docs](https://img.shields.io/badge/documentation-gray.svg)](https://more-stream-data.hexdocs.pm)

# MoreStreamData

`MoreStreamData` is a small collection of extra generators for [`StreamData`](https://hexdocs.pm/stream_data/StreamData.html), Elixir's most widely used property-based testing library.

Use it when you need generators for common application data: IP addresses, domains, URLs, email addresses, decimals, durations, datetimes, bounded integers and floats, random samples, or strings that match a regular expression.

## Install

Add `more_stream_data` to your `mix.exs`:

```elixir
def deps do
  [
    {:more_stream_data, "~> 0.8", only: :test}
  ]
end
```

## Usage
`MoreStreamData` returns regular `StreamData.t()` generators, so it works with `ExUnitProperties` and with StreamData combinators.

```elixir
defmodule AccountTest do
  use ExUnit.Case
  use ExUnitProperties

  import MoreStreamData

  property "accepts valid signup emails" do
    check all email <- email() do
      assert MyApp.Accounts.valid_email?(email)
    end
  end
end
```

### Generate Strings From a Regex

Use `from_regex/2` when your production code validates with a regex and your test should exercise matching strings.

```elixir
property "accepts valid product codes" do
  regex = ~r/^[A-Z]{3}-\d{4}$/

  check all code <- MoreStreamData.from_regex(regex) do
    assert Regex.match?(regex, code)
  end
end
```

### Network Values

`ip_address/1` generates IPv4 and IPv6 strings. Without options it includes random addresses and IANA special ranges for better edge-case coverage.

```elixir
check all ip <- MoreStreamData.ip_address(version: 4) do
  assert {:ok, _} = :inet.parse_address(String.to_charlist(ip))
end
```

Constrain generation to a CIDR range with `:network`:

```elixir
check all ip <- MoreStreamData.ip_address(network: "10.0.0.0/8") do
  assert String.starts_with?(ip, "10.")
end
```

### Generate Web Identifiers

Use `domain/1`, `url/0`, and `email/1` for practical web-facing values.

```elixir
check all domain <- MoreStreamData.domain(max_length: 80),
          email <- MoreStreamData.email(domains: StreamData.constant(domain)) do
  assert String.ends_with?(email, "@" <> domain)
end
```

`email/1` intentionally targets common provider-compatible addresses rather than the full RFC-5322 grammar.

### Extend Existing StreamData Tests

Import MoreStreamData beside `ExUnitProperties`:

```elixir
defmodule BillingTest do
  use ExUnit.Case
  use ExUnitProperties

  import MoreStreamData

  property "invoice totals are never negative" do
    check all amount <- decimal(min: Decimal.new("0"), allow_nan?: false) do
      assert Decimal.compare(amount, 0) in [:eq, :gt]
    end
  end
end
```

Because each function returns a StreamData generator, you can compose it with
your own domain-specific generators:

```elixir
def user_gen do
  StreamData.fixed_map(%{
    email: MoreStreamData.email(),
    homepage: MoreStreamData.url(),
    last_seen_at: MoreStreamData.datetime(max: DateTime.utc_now())
  })
end
```

Refer to the [Documentation](https://more-stream-data.hexdocs.pm/MoreStreamData.html) for a list of all the generators with their corresponding options an examples.

## Regex Support

`from_regex/2` is useful for many validation-style regexes, especially anchored ASCII patterns. Supported features include:

- literals, concatenation, alternation, groups, character classes, ranges, and common repetition operators
- `\d`, `\D`, `\w`, `\W`, `\s`, `\S`, blank and vertical-space classes
- anchors such as `^`, `$`, `\A`, and `\z`
- case-insensitive, extended, dotall, multiline, firstline, ungreedy, and export modifiers where they affect generated strings
- negative lookahead and negative lookbehind
- hex escapes such as `\xHH` and `\x{HHH}`

Some regex features are not implemented yet, including word boundaries, positive lookahead, positive lookbehind, Unicode mode, and newline mode settings such as `(*ANYCRLF)`.


The project aims to port useful strategies from [Python Hypothesis](https://hypothesis.readthedocs.io/en/latest/) to Elixir ecosystem.
