This walkthrough documents the canonical real integration harness at ash_jido_consumer/.
It is the reference app for validating AshJido behavior against a real Postgres data layer.
1. Local Setup
cd ash_jido_consumer
mix deps.get
mix ecto.setup
mix test
Notes:
mix testin this app also runsecto.createandecto.migratevia test aliases.- DB defaults can be overridden with
ASH_JIDO_CONSUMER_DB_*env vars.
2. Domain and Resource Layout
The consumer app is intentionally a single app with focused domains:
AshJidoConsumer.AccountsAshJidoConsumer.Accounts.User(policy-aware create/read + runtime context actions)
AshJidoConsumer.ContentAshJidoConsumer.Content.AuthorAshJidoConsumer.Content.Post(signals, telemetry, relationship read load)
AshJidoConsumer.TenantingAshJidoConsumer.Tenanting.Note(attribute multitenancy)
3. End-to-End Flows Verified
Full CRUD via generated actions
{:ok, author} =
AshJidoConsumer.Content.Author
|> Ash.Changeset.for_create(:create, %{name: "Ada"}, domain: AshJidoConsumer.Content)
|> Ash.create(domain: AshJidoConsumer.Content)
{:ok, created} =
AshJidoConsumer.Content.Post.Jido.Create.run(
%{title: "Guide Post", author_id: author.id},
%{domain: AshJidoConsumer.Content, signal_dispatch: {:noop, []}}
)
{:ok, updated} =
AshJidoConsumer.Content.Post.Jido.Update.run(
%{id: created[:id], title: "Guide Post Updated"},
%{domain: AshJidoConsumer.Content, signal_dispatch: {:noop, []}}
)
{:ok, %{deleted: true}} =
AshJidoConsumer.Content.Post.Jido.Destroy.run(
%{id: updated[:id]},
%{domain: AshJidoConsumer.Content, signal_dispatch: {:noop, []}}
)Policy actor via scope
{:ok, user} =
AshJidoConsumer.Accounts.User.Jido.Create.run(
%{name: "Scope User", email: "scope-user@example.com"},
%{domain: AshJidoConsumer.Accounts, scope: %{actor: %{id: "scope_actor"}}}
)
user[:email] # => "scope-user@example.com"Relationship-aware read load
{:ok, posts} =
AshJidoConsumer.Content.Post.Jido.Read.run(
%{},
%{domain: AshJidoConsumer.Content}
)
Enum.at(posts, 0)[:author]Signals + telemetry
:telemetry.attach_many(
"consumer-guide",
[
[:jido, :action, :ash_jido, :start],
[:jido, :action, :ash_jido, :stop],
[:jido, :action, :ash_jido, :exception]
],
fn event, measurements, metadata, _ ->
IO.inspect({event, measurements, metadata}, label: "consumer_telemetry")
end,
nil
)
{:ok, _post} =
AshJidoConsumer.Content.Post.Jido.Create.run(
%{title: "Signal Post", author_id: author_id},
%{domain: AshJidoConsumer.Content, signal_dispatch: {:pid, target: self()}}
)
assert_receive {:signal, %Jido.Signal{}}4. CI Execution Model
The root CI includes a dedicated Consumer Integration (AshPostgres) job that:
- Starts
postgres:16service. - Installs dependencies in
ash_jido_consumer/. - Runs
mix format --check-formattedinash_jido_consumer/. - Runs
mix testinash_jido_consumer/.
This keeps consumer verification behavior-driven and separate from root ETS-only test coverage.