# `Trellis.TestSupport`
[🔗](https://github.com/salesforce-misc/trellis/blob/v0.1.0-alpha/clients/elixir/lib/trellis/test_support.ex#L1)

Helpers for a test suite that loads fixtures or bulk data into tables
Trellis captures. Call them from test code only.

A fixture loader that disables triggers for the load (Rails-style
`disable_referential_integrity`, `pg_restore --disable-triggers`, a
hand-written `ALTER TABLE ... DISABLE TRIGGER ALL`) also disables
Trellis's capture triggers, so the rows it writes never reach a target,
and `ENABLE TRIGGER ALL` leaves them in a mode the staging worker treats
as a disruption of capture. Trellis then pauses every definition that
reads the table (a `Trellis.CaptureFailure` of kind `:disruption` on its
status) rather than let a test read targets that are silently wrong. A
test suite's answer is a `RESUME` after the load, which rebuilds the
targets from the tables as they are now: `resume_capture_disruptions/2`.

    setup do
      Postgrex.query!(pg, "ALTER TABLE orders DISABLE TRIGGER ALL", [])
      # ... load the fixtures ...
      Postgrex.query!(pg, "ALTER TABLE orders ENABLE TRIGGER ALL", [])

      {:ok, _resumed} = Trellis.TestSupport.resume_capture_disruptions(trellis)
      :ok
    end

It is cheap when nothing was disrupted (one catalog read per captured
table), so running it in every test's `setup` is fine.

## Ecto's SQL sandbox

Trellis sees a write when it commits. A test that writes inside a sandbox
transaction (the default `:manual` or `:shared` mode) rolls it back, so
nothing reaches a target. Load the fixtures that targets must reflect
outside the sandbox (in `setup_all`, or with the sandbox in `:auto` mode),
then call the helper once.

# `resume_capture_disruptions`

```elixir
@spec resume_capture_disruptions(Trellis.trellis(), [{:timeout_ms, non_neg_integer()}]) ::
  {:ok, [String.t()]}
  | {:error, {:resume_refused, %{required(String.t()) =&gt; String.t()}}}
  | {:error, Trellis.Error.t()}
```

Resumes every definition of `trellis` that is paused because Trellis's
capture of a table it reads was changed outside Trellis, waits until each
is `:live` again, then waits until every write committed so far has
reached its targets (`Trellis.await_converged/3`), so a test that follows
reads exact targets. Returns `{:ok, resumed}` with the resumed targets
(`"schema.table"`; `[]` when nothing was disrupted).

It runs a disruption check of its own first
(`Trellis.detect_capture_disruptions/1`), so a disruption the staging
worker has not seen yet is paused and resumed here rather than a pass
later.

It resumes only a `Trellis.CaptureFailure` of kind `:disruption`. A
definition an operator paused (`PAUSE TRANSFORM`), or one paused by a
schema change, a drain halt or a failing build, stays paused. A definition
paused because an upstream one was resumes with its upstream.

A `RESUME` the engine refuses (for example, the table a definition read is
missing, or a table define would now refuse) leaves that definition
paused. The others are still resumed and waited for; then
`{:error, {:resume_refused, refused}}` is returned, `refused` mapping each
target to the reason. Any other engine error, or a definition that is not
`:live` within `:timeout_ms` (default 30 000; a `:timeout` error), is
`{:error, %Trellis.Error{}}`.

The instance needs its staging worker and a drain worker running somewhere
(a test setup usually connects with `staging: true, drain_threads: 1`):
resuming rebuilds through them.

# `resume_capture_disruptions!`

```elixir
@spec resume_capture_disruptions!(Trellis.trellis(), [
  {:timeout_ms, non_neg_integer()}
]) :: [String.t()]
```

Like `resume_capture_disruptions/2`, but returns the resumed targets and
raises `Trellis.Error`, or `Trellis.TestSupport.ResumeRefused` when a
resume is refused.

---

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