# `Beancount.Property`
[🔗](https://github.com/thanos/beancount_ex/blob/v0.6.0/lib/beancount/property.ex#L2)

StreamData generators and helpers for property-based testing.

These generators produce *valid, balanced* Beancount data so that
properties such as "every generated ledger passes `bean-check`" and
"rendering is deterministic" can be expressed.

This module is only compiled when `StreamData` is available (the `:test`
and `:dev` environments).

## Oracle comparison

Use `Beancount.Property.compare/3` (or `Beancount.Compare.compare/3`) to
validate a native engine against the CLI oracle on identical input.

# `account`

```elixir
@spec account() :: StreamData.t(String.t())
```

Generate a valid account name such as `Assets:Bank`.

## Examples

    data = Beancount.Property.account() |> Enum.take(1) |> hd()
    String.match?(data, ~r/^(Assets|Liabilities|Equity|Income|Expenses):/)
    # => {:ok, _}

# `amount`

```elixir
@spec amount() :: StreamData.t(Decimal.t())
```

Generate a positive integer `Decimal` amount.

## Examples

    iex> amount = Beancount.Property.amount() |> Enum.take(1) |> hd()
    iex> %Decimal{} = amount
    amount

# `balanced_transaction`

```elixir
@spec balanced_transaction() :: StreamData.t(Beancount.Directives.Transaction.t())
```

Generate a balanced `Beancount.Directives.Transaction`.

The generated postings always sum to zero in a single currency, so the
transaction is guaranteed to balance.

## Examples

    iex> txn = Beancount.Property.balanced_transaction() |> Enum.take(1) |> hd()
    iex> %Beancount.Directives.Transaction{} = txn
    txn

# `compare`

```elixir
@spec compare(module(), module(), Beancount.directive() | binary()) ::
  {:ok, :equivalent} | {:error, Beancount.Property.Diff.t()}
```

Compare two engines on identical input within the parity contract enforced
by `Beancount.Compare.compare/3`.

Delegates to `Beancount.Compare.compare/3`.

## Examples

    iex> ledger = [
    ...>   Beancount.open(~D[2026-01-01], "Assets:Bank", ["USD"]),
    ...>   Beancount.open(~D[2026-01-01], "Income:Salary", ["USD"]),
    ...>   Beancount.open(~D[2026-01-01], "Equity:Opening", ["USD"]),
    ...>   Beancount.transaction(~D[2026-01-31], "*", nil, "Salary", [
    ...>     Beancount.posting("Assets:Bank", Decimal.new("100"), "USD"),
    ...>     Beancount.posting("Income:Salary", Decimal.new("-100"), "USD")
    ...>   ])
    ...> ]
    iex> Beancount.Property.compare(Beancount.Engine.Elixir, Beancount.Engine.Elixir, ledger)
    {:ok, :equivalent}

# `currency`

```elixir
@spec currency() :: StreamData.t(String.t())
```

Generate a commodity/currency code.

## Examples

    iex> data = Beancount.Property.currency() |> Enum.take(1) |> hd()
    iex> data in ~w(USD EUR GBP CAD CHF JPY)
    true

# `date`

```elixir
@spec date() :: StreamData.t(Date.t())
```

Generate a `Date` within a bounded range for determinism.

## Examples

    iex> date = Beancount.Property.date() |> Enum.take(1) |> hd()
    iex> %Date{} = date
    date

# `ledger`

```elixir
@spec ledger() :: StreamData.t([Beancount.Directive.t()])
```

Generate a complete, valid ledger: `open` directives for every account
used, followed by a balanced transaction.

## Examples

    iex> ledger = Beancount.Property.ledger() |> Enum.take(1) |> hd()
    iex> is_list(ledger)
    true

# `metadata`

```elixir
@spec metadata() :: StreamData.t(map())
```

Generate a small metadata map with string values.

## Examples

    iex> map = Beancount.Property.metadata() |> Enum.take(1) |> hd()
    iex> is_map(map)
    true

---

*Consult [api-reference.md](api-reference.md) for complete listing*
