# `Accrue.Processor.Fake`
[🔗](https://github.com/szTheory/accrue/blob/accrue-v0.3.0/lib/accrue/processor/fake.ex#L1)

Deterministic in-memory `Accrue.Processor` adapter for tests and demos.

The Fake is Accrue's **primary test surface**. It implements
the `Accrue.Processor` behaviour entirely in-process with a GenServer +
struct state:

- **Deterministic ids** per resource with 5-digit zero-padded counters:
  `cus_fake_00001`, `sub_fake_00001`, `in_fake_00001`, `pi_fake_00001`,
  `si_fake_00001`, `pm_fake_00001`, `ch_fake_00001`, `re_fake_00001`.
- **Test clock** — all timestamps derive from an in-memory clock that
  starts at `Accrue.Processor.Fake.State.epoch/0` and moves only via
  `advance/2` (or `advance_subscription/2` for subscription-aware
  clock crossing), mirroring Stripe test-clock semantics.
- **Clean reset** — `reset/0` zeros all counters, clears state, and
  restores the clock to the epoch. Call in `setup` blocks.
- **Scripted responses** — `scripted_response/2` programs a one-shot
  return value for a named op so tests can simulate processor failures
  (card declined, rate limit) without mocking.
- **Subscription transitions** — `transition/3` moves a subscription to
  any status, optionally synthesizing `customer.subscription.updated`
  webhooks in-process.
- **Trial crossing** — `advance_subscription/2` advances the clock and,
  if the crossing period includes `trial_end - 3d` or `trial_end`,
  synthesizes `customer.subscription.trial_will_end` or
  `customer.subscription.updated` (status→active) events.

## Startup

The Fake is a `GenServer` with a fixed name (`__MODULE__`). It is **not**
started by `Accrue.Application` — tests that need it call:

    setup do
      case Accrue.Processor.Fake.start_link([]) do
        {:ok, _} -> :ok
        {:error, {:already_started, _}} -> :ok
      end

      :ok = Accrue.Processor.Fake.reset()
      :ok
    end

## Id prefixes

Prefixes are module attributes so they are greppable and future-proof:

    @customer_prefix     "cus_fake_"
    @subscription_prefix "sub_fake_"
    @invoice_prefix      "in_fake_"
    @payment_intent_prefix "pi_fake_"
    @setup_intent_prefix   "si_fake_"
    @payment_method_prefix "pm_fake_"
    @charge_prefix         "ch_fake_"
    @refund_prefix         "re_fake_"
    @event_prefix          "evt_fake_"

# `accounts`

```elixir
@spec accounts() :: [map()]
```

Returns all stored connect accounts (always platform-scoped — connected
accounts are never themselves nested under another connected account).

# `advance`

```elixir
@spec advance(GenServer.server(), integer()) :: :ok
```

Advances the in-memory clock by `seconds` seconds. Existing Phase 1
API — preserved for tests that only need to push the clock without
any subscription-aware webhook synthesis.

Accepts an optional server argument for tests that explicitly name the
GenServer.

# `advance_subscription`

```elixir
@spec advance_subscription(
  String.t() | nil,
  keyword()
) :: :ok
```

Subscription-aware clock advance (D3-82). Advances the Fake clock by
`opts[:days] * 86400 + opts[:seconds]` and, if `stripe_id` references
a subscription with a `trial_end`, synthesizes:

- `customer.subscription.trial_will_end` when crossing `trial_end - 3d`
- `customer.subscription.updated` (with `status: :active`) when
  crossing `trial_end`

Pass `synthesize_webhooks: false` to skip the in-process event
dispatch (useful for tests that only care about the state side
effects).

# `call_count`

```elixir
@spec call_count(atom()) :: non_neg_integer()
```

Returns the number of times `callback` has been invoked against this
Fake since the last `reset/0`. Used by Phase 5 Plan 05 tests to
count distinct processor calls through `separate_charge_and_transfer`.

# `charges_on`

```elixir
@spec charges_on(String.t() | :platform) :: [map()]
```

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `current_time`

```elixir
@spec current_time(GenServer.server()) :: DateTime.t()
```

Returns the current in-memory clock value.

# `customers_on`

```elixir
@spec customers_on(String.t() | :platform) :: [map()]
```

Returns all customers stored under `scope`. Scope is either a
binary `"acct_..."` (connected account) or the `:platform` atom
(no `with_account/2` wrapper).

# `id_prefixes`

```elixir
@spec id_prefixes() :: %{required(atom()) =&gt; String.t()}
```

Returns the id prefix map for the Fake adapter (D-20). Phase 3 resource
types already have counter slots and prefixes reserved here so growing
the callback list never churns id shapes.

# `meter_events_for`

```elixir
@spec meter_events_for(Accrue.Billing.Customer.t() | String.t()) :: [map()]
```

Returns the Fake-stored meter events for the given customer (by
`processor_id`) in insertion order. Test helper only — the Fake never
exposes meter events through the behaviour (Stripe doesn't either).

# `now`

```elixir
@spec now() :: DateTime.t()
```

Alias of `current_time/0` — the canonical name used by `Accrue.Clock`
when the runtime env is `:test` (D3-86). Kept as a thin wrapper so
callers don't have to remember to pass a server argument, and so the
grep pattern `Fake.now` is stable across the codebase.

# `reset`

```elixir
@spec reset() :: :ok
```

Resets all counters, stored resources, scripts, and the clock.

# `reset_preserve_connect`

```elixir
@spec reset_preserve_connect() :: :ok
```

Full reset like `reset/0`, but preserves Connect account rows and the
`:connect_account` counter.

`Accrue.BillingCase` uses this in `setup/1` so async billing tests do not
wipe in-memory Connect state while `Accrue.ConnectCase` (or other modules)
are mid-flight on the shared named Fake GenServer.

# `scripted_response`

```elixir
@spec scripted_response(atom(), {:ok, map()} | {:error, Exception.t()}) :: :ok
```

Pre-programs a one-shot return value for the named op. The next call
to that op consumes the scripted response; subsequent calls fall back
to the default in-memory behaviour.

    Fake.scripted_response(:create_subscription, {:error, %Accrue.CardError{...}})

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Starts the Fake processor with a fixed name.

# `stub`

```elixir
@spec stub(atom(), (... -&gt; term())) :: :ok
```

Overrides one behaviour callback with a custom function for the lifetime
of the GenServer (until `reset/0`). Intended for per-test stubbing.

# `subscriptions_on`

```elixir
@spec subscriptions_on(String.t() | :platform) :: [map()]
```

# `transfers_on`

```elixir
@spec transfers_on(String.t() | :platform) :: [map()]
```

Returns all stored transfers filtered by scope. Transfers are always
platform-scoped (the platform is the party initiating the transfer),
but the filter parameter is accepted for API symmetry with the other
`*_on/1` helpers.

# `transition`

```elixir
@spec transition(String.t(), atom(), keyword()) ::
  {:ok, map()} | {:error, Accrue.APIError.t()}
```

Transitions a stored subscription to `new_status`. By default
synthesizes a `customer.subscription.updated` event in-process; pass
`synthesize_webhooks: false` to skip.

---

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