mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 13:33:20 +12:00
170 lines
4 KiB
Elixir
170 lines
4 KiB
Elixir
defmodule Ash.Test.Actions.AtomicUpdateTest do
|
|
@moduledoc false
|
|
use ExUnit.Case, async: true
|
|
|
|
require Ash.Query
|
|
require Ash.Expr
|
|
|
|
defmodule Atomic do
|
|
use Ash.Resource.Change
|
|
|
|
def atomic(_, _, _) do
|
|
:ok
|
|
end
|
|
end
|
|
|
|
defmodule NotAtomic do
|
|
use Ash.Resource.Change
|
|
|
|
def change(changeset, _, _) do
|
|
changeset
|
|
end
|
|
end
|
|
|
|
defmodule Author do
|
|
@moduledoc false
|
|
use Ash.Resource, data_layer: Ash.DataLayer.Ets
|
|
|
|
ets do
|
|
private?(true)
|
|
end
|
|
|
|
actions do
|
|
defaults [:create, :read, :update, :destroy]
|
|
|
|
update :only_allow_name do
|
|
accept([:name])
|
|
end
|
|
|
|
update :with_validation do
|
|
accept([:name])
|
|
|
|
validate attribute_equals(:name, "fred")
|
|
validate compare(:score, greater_than_or_equal_to: 0, less_than_or_equal_to: 10)
|
|
end
|
|
|
|
update :increment_score do
|
|
accept []
|
|
change increment(:score, amount: 1, overflow_limit: 5), always_atomic?: true
|
|
end
|
|
|
|
update :sometimes_atomic do
|
|
accept []
|
|
require_atomic? true
|
|
change Atomic
|
|
end
|
|
end
|
|
|
|
attributes do
|
|
uuid_primary_key :id
|
|
attribute :name, :string
|
|
attribute :bio, :string
|
|
attribute :score, :integer
|
|
end
|
|
|
|
code_interface do
|
|
define_for Ash.Test.Actions.AtomicUpdateTest.Api
|
|
define :increment_score
|
|
define :sometimes_atomic
|
|
end
|
|
end
|
|
|
|
defmodule Api do
|
|
@moduledoc false
|
|
use Ash.Api
|
|
|
|
resources do
|
|
resource Author
|
|
end
|
|
end
|
|
|
|
test "atomics can be added to a changeset" do
|
|
author =
|
|
Author
|
|
|> Ash.Changeset.new(%{name: "fred"})
|
|
|> Api.create!()
|
|
|
|
author =
|
|
author
|
|
|> Ash.Changeset.for_update(:only_allow_name)
|
|
|> Ash.Changeset.atomic_update(:name, Ash.Expr.expr(name <> " weasley"))
|
|
|> Api.update!()
|
|
|
|
assert author.name == "fred weasley"
|
|
end
|
|
|
|
test "a changeset can be fully atomic" do
|
|
changeset =
|
|
Ash.Changeset.fully_atomic_changeset(Author, :with_validation, %{name: "fred weasly"},
|
|
eager?: false
|
|
)
|
|
|
|
assert changeset.valid?
|
|
assert changeset.atomics[:name]
|
|
end
|
|
|
|
test "values are eagerly validated" do
|
|
changeset =
|
|
Ash.Changeset.fully_atomic_changeset(Author, :with_validation, %{name: "fred weasly"})
|
|
|
|
refute changeset.valid?
|
|
end
|
|
|
|
test "policies that require original data" do
|
|
author =
|
|
Author
|
|
|> Ash.Changeset.new(%{name: "fred", score: 0})
|
|
|> Api.create!()
|
|
|
|
assert Author.increment_score!(author, authorize?: true).score == 1
|
|
end
|
|
|
|
# ets doesn't support atomics, so we can't do this here
|
|
# test "an action that cannot be done fully atomically raises an error at runtime" do
|
|
# author =
|
|
# Author
|
|
# |> Ash.Changeset.new(%{name: "fred", score: 0})
|
|
# |> Api.create!()
|
|
|
|
# assert_raise Ash.Error.Framework, ~r/must be performed atomically/, fn ->
|
|
# Author.sometimes_atomic!(author, %{atomic: false})
|
|
# end
|
|
# end
|
|
|
|
# See the note in `Ash.Resource.Verifiers.VerifyActionsAtomic` for why we can't introduce
|
|
# this yet.
|
|
# test "resource compilation fails if an action is known to not be able to be made atomic" do
|
|
# assert_raise Spark.Error.DslError, ~r/can never be done atomically/, fn ->
|
|
# defmodule ExampleNonAtomic do
|
|
# use Ash.Resource
|
|
|
|
# attributes do
|
|
# uuid_primary_key :id
|
|
# end
|
|
|
|
# actions do
|
|
# update :update do
|
|
# require_atomic? true
|
|
# change NotAtomic
|
|
# end
|
|
# end
|
|
# end
|
|
# end
|
|
# end
|
|
|
|
describe "increment/1" do
|
|
test "it increments the value, honoring overflow" do
|
|
author =
|
|
Author
|
|
|> Ash.Changeset.new(%{name: "fred", score: 0})
|
|
|> Api.create!()
|
|
|
|
assert Author.increment_score!(author).score == 1
|
|
assert Author.increment_score!(author).score == 2
|
|
assert Author.increment_score!(author).score == 3
|
|
assert Author.increment_score!(author).score == 4
|
|
assert Author.increment_score!(author).score == 5
|
|
assert Author.increment_score!(author).score == 1
|
|
end
|
|
end
|
|
end
|