ash/test/reactor/update_test.exs
James Harton 86d74ed789
feat(Ash.Reactor): Add a Reactor extension that makes working with resources easy. (#683)
* feat: Add `Ash.Reactor` with create support.

* improvement: Add `Ash.Reactor` update support.

* improvement: Add `Ash.Reactor` destroy support.

* improvement(Ash.Reactor): Support for transactional handling of notifications.

* improvement(Ash.Reactor): Add `read` and `get` steps.

* docs: Add the beginnings of a Reactor topic.

* improvement(Ash.Reactor): add support for generic actions.

* improvement: Add `undo` capability to `create` step.

* improvement: transaction and undo working.

* docs: Start documenting undo behaviour.

* chore: Update to Reactor 0.6.

* improvement: Automatically thread Ash tracers through Reactor.

* improvement(Ash.Reactor): Add undo to generic actions.

* docs: Improve reactor documentation.

* fix: Mimic copying `Ash.Notifier` seems to break the compiler for some reason.
2024-03-02 10:26:25 +13:00

179 lines
3.9 KiB
Elixir

defmodule Ash.Test.ReactorUpdateTest do
@moduledoc false
use ExUnit.Case, async: true
defmodule Post do
@moduledoc false
use Ash.Resource, data_layer: Ash.DataLayer.Ets
ets do
private? true
end
attributes do
uuid_primary_key :id
attribute :title, :string, allow_nil?: false
attribute :sub_title, :string
attribute :published, :boolean, default: false
end
actions do
defaults [:create, :read, :update, :destroy]
end
code_interface do
define_for Ash.Test.ReactorUpdateTest.Api
define :create
define :get, action: :read, get_by: :id
end
end
defmodule Api do
@moduledoc false
use Ash.Api
resources do
resource Post
end
end
test "it can update a post" do
defmodule SimpleUpdatePostReactor do
@moduledoc false
use Reactor, extensions: [Ash.Reactor]
ash do
default_api Api
end
input :post
update :publish_post, Post, :update do
initial(input(:post))
inputs(%{published: value(true)})
end
end
{:ok, %{published: false} = original_post} =
Post.create(%{title: "Title", sub_title: "Sub-title"})
assert {:ok, post} =
Reactor.run(SimpleUpdatePostReactor, %{post: original_post}, %{}, async?: false)
assert post.published
end
test "it defaults to the primary action when the action is not supplied" do
defmodule InferredActionNameUpdatePostReactor do
@moduledoc false
use Reactor, extensions: [Ash.Reactor]
ash do
default_api Api
end
input :post
input :new_title
update :update_post, Post do
inputs(%{title: input(:new_title)})
initial(input(:post))
end
end
{:ok, original_post} =
Post.create(%{title: "Title", sub_title: "Sub-title"})
assert {:ok, post} =
Reactor.run(
InferredActionNameUpdatePostReactor,
%{post: original_post, new_title: "New Title"},
%{},
async?: false
)
assert post.title == "New Title"
end
test "it merges multiple `inputs` entities together" do
defmodule MergedInputsCreatePostReactor do
@moduledoc false
use Ash.Reactor, extensions: [Ash.Reactor]
ash do
default_api Api
end
input :post
input :new_title
input :new_sub_title
update :update_post, Post, :update do
initial(input(:post))
inputs(%{title: input(:new_title)})
inputs(%{sub_title: input(:new_sub_title)})
end
end
{:ok, original_post} =
Post.create(%{title: "Title", sub_title: "Sub-title"})
assert {:ok, post} =
Reactor.run(
MergedInputsCreatePostReactor,
%{post: original_post, new_title: "New Title", new_sub_title: "New Sub-title"},
%{},
async?: false
)
assert post.title == "New Title"
assert post.sub_title == "New Sub-title"
end
test "it can undo the update on error" do
defmodule UndoingUpdateReactor do
@moduledoc false
use Ash.Reactor
ash do
default_api Api
end
input :post
input :new_title
update :update_post, Post, :update do
initial(input(:post))
inputs(%{title: input(:new_title)})
undo :always
undo_action(:update)
end
step :fail do
wait_for :update_post
run fn _, _ ->
assert [] = Api.read!(Post)
raise "hell"
end
end
end
{:ok, post} = Post.create(%{title: "Title"})
assert {:error, _} =
Reactor.run(
UndoingUpdateReactor,
%{
post: post,
new_title: "New title"
},
%{},
async?: false
)
post_run_post = Post.get!(post.id)
assert post_run_post.title == "New title"
end
end