# `AshNeo4j.Sandbox`
[🔗](https://github.com/diffo-dev/ash_neo4j/blob/v0.7.0/lib/sandbox.ex#L5)

Test sandbox for AshNeo4j, analogous to `Ecto.Adapters.SQL.Sandbox`.

Each test that calls `checkout/0` gets a dedicated Neo4j connection with an
open transaction. Every Cypher query executed by that test process runs
inside that transaction. When the test process exits the transaction is
rolled back automatically, so no data persists between tests and safe
parallel execution (`async: true`) is possible.

## Usage

Replace the `Neo4jHelper.delete_all()` pattern with a sandbox checkout:

    setup do
      AshNeo4j.Sandbox.checkout()
      on_exit(&AshNeo4j.Sandbox.rollback/0)
    end

The `on_exit` call is optional. ExUnit's on_exit callbacks run in a separate
process after the test process has already exited, so by the time
`rollback/0` is called the transaction has already been rolled back via the
holder's exit-signal trap. Including `on_exit(&AshNeo4j.Sandbox.rollback/0)`
is harmless (it becomes a no-op) and is recommended for clarity.

## How it works

`checkout/0` spawns a *holder* process linked to the test process. The holder
opens a `Bolty.transaction/3` and waits for query messages. All
`Cypher.run/2` calls from the test process are forwarded to the holder and
executed on the single sandboxed connection, keeping every write inside the
uncommitted transaction.

When the test process exits (pass, fail, or crash) the link delivers an exit
signal to the holder. Because the holder traps exits, the signal becomes a
message that triggers `Bolty.rollback/2`, cleanly rolling back the
transaction and returning the connection to the pool.

## Parallel tests

Because each test's writes are confined to an isolated transaction that is
never committed, concurrent tests cannot interfere with each other:

    use ExUnit.Case, async: true

    setup do
      AshNeo4j.Sandbox.checkout()
      on_exit(&AshNeo4j.Sandbox.rollback/0)
    end

# `checkout`

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

Checks out a sandbox connection and begins a Neo4j transaction.

All Cypher queries from the calling process are routed to this connection and
executed within the open transaction. The transaction is rolled back
automatically when the calling process exits.

Raises if called more than once without an intervening `rollback/0`.

# `rollback`

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

Rolls back the sandbox transaction for the current process.

When called from the same process as `checkout/0` this signals the holder to
roll back immediately and blocks until the rollback completes.

When called from an `on_exit` callback (which runs in a different process
after the test process has already exited) this is a safe no-op — the
transaction has already been rolled back via the holder's exit-signal trap.

---

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