# `DoubleDown.Repo.Stub`
[🔗](https://github.com/mccraigmccraig/double_down/blob/main/lib/double_down/repo/stub.ex#L7)

Stateless stub for `DoubleDown.Repo`.

Write operations (`insert`, `update`, `delete`) apply changeset
changes and return `{:ok, struct}` but store nothing. Read
operations go through an optional fallback function, or raise a
clear error.

Implements `DoubleDown.Contract.Dispatch.StubHandler`, so it can
be used by module name with `Double.stub`:

## Usage with Double.stub

    # Writes only — reads will raise with a helpful message:
    DoubleDown.Double.stub(DoubleDown.Repo, DoubleDown.Repo.Stub)

    # With fallback for specific reads:
    DoubleDown.Double.stub(DoubleDown.Repo, DoubleDown.Repo.Stub,
      fn
        :get, [User, 1] -> %User{id: 1, name: "Alice"}
        :all, [User] -> [%User{id: 1, name: "Alice"}]
        :exists?, [User] -> true
      end
    )

    # Layer expects on top for failure simulation:
    DoubleDown.Repo
    |> DoubleDown.Double.stub(DoubleDown.Repo.Stub)
    |> DoubleDown.Double.expect(:insert, fn [changeset] ->
      {:error, Ecto.Changeset.add_error(changeset, :email, "taken")}
    end)

## When to use Repo.Stub

Use `Repo.Stub` when your test only needs fire-and-forget writes
and a few canned read responses. It's the lightest-weight option —
no state to reason about.

For read-after-write consistency, use `Repo.InMemory` (closed-world,
recommended) or `Repo.OpenInMemory` (open-world, fallback-based).

| Fake | State | Reads |
|------|-------|-------|
| `Repo.Stub` | None | Fallback function or raise |
| `Repo.InMemory` | Complete store | Authoritative for bare schemas |
| `Repo.OpenInMemory` | Partial store | PK lookup in state, fallback for rest |

# `new`

```elixir
@spec new(
  (atom(), [term()] -&gt; term()) | nil,
  keyword()
) :: (atom(), [term()] -&gt; term())
```

Create a new Test handler function.

Returns a 2-arity function `(operation, args) -> result` suitable for
use with `DoubleDown.Double.stub/2` or `DoubleDown.Testing.set_fn_handler/2`.

## Arguments

  * `fallback_fn` — an optional 2-arity function `(operation, args) -> result`
    that handles read operations. If the function raises `FunctionClauseError`
    (no matching clause), dispatch falls through to an error. If omitted or
    `nil`, all reads raise immediately.
  * `opts` — keyword options (reserved for future use).

## Examples

    # Writes only — via module name (StubHandler)
    DoubleDown.Double.stub(DoubleDown.Repo, DoubleDown.Repo.Stub)

    # With fallback for specific reads
    DoubleDown.Double.stub(DoubleDown.Repo, DoubleDown.Repo.Stub,
      fn
        :get, [User, 1] -> %User{id: 1, name: "Alice"}
        :all, [User] -> [%User{id: 1, name: "Alice"}]
        :exists?, [User] -> true
      end
    )

## Legacy keyword-only form (still supported)

    DoubleDown.Repo.Stub.new(fallback_fn: fn :get, [User, 1] -> %User{} end)

---

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