ash/test/reactor/update_test.exs

188 lines
4.5 KiB
Elixir

defmodule Ash.Test.ReactorUpdateTest do
@moduledoc false
use ExUnit.Case, async: true
alias Ash.Test.Domain
defmodule Post do
@moduledoc false
use Ash.Resource, data_layer: Ash.DataLayer.Ets, domain: Domain
ets do
private? true
end
attributes do
uuid_primary_key :id
attribute :title, :string, allow_nil?: false, public?: true
attribute :sub_title, :string, public?: true
attribute :published, :boolean, default: false, public?: true
end
actions do
default_accept :*
defaults [:read, :destroy, create: :*, update: :*]
update :undo_update_post do
require_atomic? false
argument :changeset, :struct do
constraints instance_of: Ash.Changeset
end
change fn changeset, _ ->
original_changeset = Ash.Changeset.get_argument(changeset, :changeset)
[:title, :sub_title, :published]
|> Enum.reduce(changeset, fn key, changeset ->
original_value = Ash.Changeset.get_data(original_changeset, key)
Ash.Changeset.change_attribute(changeset, key, original_value)
end)
end
end
end
code_interface do
define :create
define :get, action: :read, get_by: :id
end
end
test "it can update a post" do
defmodule SimpleUpdatePostReactor do
@moduledoc false
use Reactor, extensions: [Ash.Reactor]
ash do
default_domain(Domain)
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_domain(Domain)
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_domain(Domain)
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_domain(Domain)
end
input :post
input :new_title
update :update_post, Post, :update do
initial(input(:post))
inputs(%{title: input(:new_title)})
undo :always
undo_action(:undo_update_post)
end
step :fail do
wait_for :update_post
run fn _, _ ->
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 == "Title"
end
end