# Stateless stub Repo handler for tests. # # Thin wrapper around DoubleDown.Repo.Stub providing skuld's # effect-based handler API. # defmodule Skuld.Repo.Stub do @moduledoc """ Stateless stub Repo handler for tests. Thin wrapper around `DoubleDown.Repo.Stub` that provides skuld's handler API. 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. ## Usage alias Skuld.Effects.Port alias Skuld.Repo # Writes only — reads will raise: comp |> Port.with_fn_handler(Repo.Stub.new()) |> Comp.run!() # With fallback for reads: comp |> Port.with_fn_handler(Repo.Stub.new( fallback_fn: fn :get, [User, 1] -> %User{id: 1, name: "Alice"} :all, [User] -> [%User{id: 1, name: "Alice"}] end )) |> Comp.run!() ## Differences from Repo.InMemory / Repo.OpenInMemory `Repo.Stub` is stateless — writes apply changesets and return `{:ok, struct}` but nothing is stored. There is no read-after-write consistency. Use `Repo.Stub` when you only need fire-and-forget writes. ## Fallback function The fallback function receives `(operation, args)` — the skuld-idiomatic 2-arity convention. The wrapper adapts it to DD's 3-arity `(contract, operation, args)` convention internally. """ alias DoubleDown.Repo.Stub, as: DDStub @doc """ Create a new Stub handler function. Returns a 3-arity function `(contract, operation, args) -> result` suitable for use with `Port.with_fn_handler/2`. ## Options * `:fallback_fn` - a 2-arity function `(operation, args) -> result` that handles read operations. ## Examples Repo.Stub.new() Repo.Stub.new( fallback_fn: fn :get, [User, 1] -> %User{id: 1, name: "Alice"} :all, [User] -> [%User{id: 1, name: "Alice"}] end ) """ @spec new(keyword()) :: (module(), atom(), [term()] -> term()) def new(opts \\ []) do skuld_fallback = Keyword.get(opts, :fallback_fn, nil) dd_fallback = if skuld_fallback do fn _contract, op, args -> skuld_fallback.(op, args) end else nil end DDStub.new(dd_fallback) end end