2022-06-22 13:00:47 +12:00
|
|
|
defmodule Ash.Test.GeneratorTest do
|
|
|
|
@moduledoc false
|
|
|
|
use ExUnit.Case, async: true
|
2022-09-21 11:44:04 +12:00
|
|
|
use ExUnitProperties
|
2022-06-22 13:00:47 +12:00
|
|
|
import Ash.Seed
|
|
|
|
require Ash.Query
|
2024-03-28 09:06:40 +13:00
|
|
|
alias Ash.Test.Domain, as: Domain
|
2022-06-22 13:00:47 +12:00
|
|
|
|
|
|
|
defmodule Author do
|
|
|
|
@moduledoc false
|
|
|
|
use Ash.Resource,
|
2024-03-28 09:06:40 +13:00
|
|
|
domain: Domain,
|
2022-06-22 13:00:47 +12:00
|
|
|
data_layer: Ash.DataLayer.Ets
|
|
|
|
|
|
|
|
ets do
|
|
|
|
private?(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
actions do
|
2024-03-28 09:06:40 +13:00
|
|
|
default_accept :*
|
|
|
|
defaults [:read, :destroy, create: :*, update: :*]
|
2022-06-22 13:00:47 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
attributes do
|
|
|
|
uuid_primary_key :id
|
2024-03-28 09:06:40 +13:00
|
|
|
attribute :name, :string, default: "Fred", public?: true
|
2023-06-03 00:18:02 +12:00
|
|
|
|
2023-06-06 00:57:22 +12:00
|
|
|
attribute :metadata, :map do
|
2024-03-28 09:06:40 +13:00
|
|
|
public?(true)
|
2023-06-06 00:57:22 +12:00
|
|
|
allow_nil? true
|
|
|
|
end
|
|
|
|
|
2023-06-03 00:18:02 +12:00
|
|
|
attribute :meta, :map do
|
2024-03-28 09:06:40 +13:00
|
|
|
public?(true)
|
2023-06-03 00:18:02 +12:00
|
|
|
allow_nil? false
|
|
|
|
end
|
2022-06-22 13:00:47 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
relationships do
|
2024-03-28 09:06:40 +13:00
|
|
|
has_many :posts, Ash.Test.GeneratorTest.Post,
|
|
|
|
destination_attribute: :author_id,
|
|
|
|
public?: true
|
2022-06-22 13:00:47 +12:00
|
|
|
|
|
|
|
has_one :latest_post, Ash.Test.GeneratorTest.Post,
|
2022-08-16 06:00:02 +12:00
|
|
|
destination_attribute: :author_id,
|
2024-03-28 09:06:40 +13:00
|
|
|
sort: [inserted_at: :desc],
|
|
|
|
public?: true
|
2022-06-22 13:00:47 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defmodule Post do
|
|
|
|
@moduledoc false
|
2024-03-28 09:06:40 +13:00
|
|
|
use Ash.Resource, domain: Domain, data_layer: Ash.DataLayer.Ets
|
2022-06-22 13:00:47 +12:00
|
|
|
|
|
|
|
ets do
|
|
|
|
private?(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
actions do
|
2024-03-28 09:06:40 +13:00
|
|
|
default_accept :*
|
|
|
|
defaults [:read, :destroy, create: :*, update: :*]
|
2022-06-22 13:00:47 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
attributes do
|
|
|
|
uuid_primary_key :id
|
2024-03-28 09:06:40 +13:00
|
|
|
|
|
|
|
attribute :title, :string do
|
|
|
|
public?(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
attribute :contents, :string do
|
|
|
|
public?(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
attribute :category, :string do
|
|
|
|
public?(true)
|
|
|
|
end
|
|
|
|
|
2022-06-22 13:00:47 +12:00
|
|
|
timestamps()
|
|
|
|
end
|
|
|
|
|
|
|
|
relationships do
|
2024-03-28 09:06:40 +13:00
|
|
|
belongs_to :author, Author, public?: true
|
2022-06-22 13:00:47 +12:00
|
|
|
|
2024-03-28 09:06:40 +13:00
|
|
|
has_many :ratings, Ash.Test.GeneratorTest.Rating, public?: true
|
2022-06-22 13:00:47 +12:00
|
|
|
|
|
|
|
many_to_many :categories, Ash.Test.GeneratorTest.Category,
|
|
|
|
through: Ash.Test.GeneratorTest.PostCategory,
|
2022-08-16 06:00:02 +12:00
|
|
|
destination_attribute_on_join_resource: :category_id,
|
2024-03-28 09:06:40 +13:00
|
|
|
source_attribute_on_join_resource: :post_id,
|
|
|
|
public?: true
|
2022-06-22 13:00:47 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defmodule PostCategory do
|
|
|
|
@moduledoc false
|
2024-03-28 09:06:40 +13:00
|
|
|
use Ash.Resource, domain: Domain, data_layer: Ash.DataLayer.Ets
|
2022-06-22 13:00:47 +12:00
|
|
|
|
|
|
|
ets do
|
|
|
|
private?(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
actions do
|
2024-03-28 09:06:40 +13:00
|
|
|
default_accept :*
|
|
|
|
defaults [:read, :destroy, create: :*, update: :*]
|
2022-06-22 13:00:47 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
relationships do
|
2024-03-28 09:06:40 +13:00
|
|
|
belongs_to :post, Post, primary_key?: true, allow_nil?: false, public?: true
|
2022-06-22 13:00:47 +12:00
|
|
|
|
|
|
|
belongs_to :category, Ash.Test.GeneratorTest.Category,
|
|
|
|
primary_key?: true,
|
2024-03-28 09:06:40 +13:00
|
|
|
allow_nil?: false,
|
|
|
|
public?: true
|
2022-06-22 13:00:47 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defmodule Category do
|
|
|
|
@moduledoc false
|
2024-03-28 09:06:40 +13:00
|
|
|
use Ash.Resource, domain: Domain, data_layer: Ash.DataLayer.Ets
|
2022-06-22 13:00:47 +12:00
|
|
|
|
|
|
|
ets do
|
|
|
|
private?(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
identities do
|
2024-03-28 09:06:40 +13:00
|
|
|
identity :unique_name, [:name], pre_check_with: Domain
|
2022-06-22 13:00:47 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
actions do
|
2024-03-28 09:06:40 +13:00
|
|
|
default_accept :*
|
|
|
|
defaults [:read, :destroy, create: :*, update: :*]
|
2022-06-22 13:00:47 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
attributes do
|
|
|
|
uuid_primary_key :id
|
2024-03-28 09:06:40 +13:00
|
|
|
|
|
|
|
attribute :name, :string do
|
|
|
|
public?(true)
|
|
|
|
end
|
2022-06-22 13:00:47 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
relationships do
|
|
|
|
many_to_many :posts, Post,
|
2024-03-28 09:06:40 +13:00
|
|
|
public?: true,
|
2022-06-22 13:00:47 +12:00
|
|
|
through: PostCategory,
|
2022-08-16 06:00:02 +12:00
|
|
|
destination_attribute_on_join_resource: :post_id,
|
|
|
|
source_attribute_on_join_resource: :category_id
|
2022-06-22 13:00:47 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defmodule Rating do
|
|
|
|
use Ash.Resource,
|
2024-03-28 09:06:40 +13:00
|
|
|
domain: Domain,
|
2022-06-22 13:00:47 +12:00
|
|
|
data_layer: Ash.DataLayer.Ets
|
|
|
|
|
|
|
|
ets do
|
|
|
|
private? true
|
|
|
|
end
|
|
|
|
|
|
|
|
attributes do
|
|
|
|
uuid_primary_key :id
|
2024-03-28 09:06:40 +13:00
|
|
|
|
|
|
|
attribute :rating, :integer do
|
|
|
|
public?(true)
|
|
|
|
end
|
2022-06-22 13:00:47 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
actions do
|
2024-03-28 09:06:40 +13:00
|
|
|
default_accept :*
|
|
|
|
defaults [:read, :destroy, create: :*, update: :*]
|
2022-06-22 13:00:47 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
relationships do
|
|
|
|
belongs_to :post, Post do
|
2024-03-28 09:06:40 +13:00
|
|
|
public?(true)
|
|
|
|
domain(Ash.Test.GeneratorTest.Category)
|
2022-06-22 13:00:47 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "action_input" do
|
|
|
|
test "action input can be provided to an action" do
|
|
|
|
check all(input <- Ash.Generator.action_input(Post, :create)) do
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, input)
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.create!()
|
2022-06-22 13:00:47 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
test "overrides are applied" do
|
2023-06-03 00:18:02 +12:00
|
|
|
check all(
|
|
|
|
input <-
|
|
|
|
Ash.Generator.action_input(Post, :create, %{title: "text"})
|
|
|
|
) do
|
2022-06-22 13:00:47 +12:00
|
|
|
post =
|
|
|
|
Post
|
|
|
|
|> Ash.Changeset.for_create(:create, input)
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.create!()
|
2022-06-22 13:00:47 +12:00
|
|
|
|
|
|
|
assert post.title == "text"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-12-21 14:42:41 +13:00
|
|
|
test "string generator honors trim?: true" do
|
|
|
|
check all(string <- Ash.Type.String.generator(min_length: 5, trim?: true)) do
|
|
|
|
assert String.length(String.trim(string)) >= 5
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-06-22 13:00:47 +12:00
|
|
|
describe "changeset" do
|
2022-10-05 09:01:42 +13:00
|
|
|
test "a directly usable changeset can be created" do
|
2022-06-22 13:00:47 +12:00
|
|
|
Post
|
|
|
|
|> Ash.Generator.changeset(:create)
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Ash.create!()
|
2022-06-22 13:00:47 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
test "many changesets can be generated" do
|
|
|
|
posts =
|
|
|
|
Post
|
|
|
|
|> Ash.Generator.many_changesets(:create, 5)
|
2024-03-28 09:06:40 +13:00
|
|
|
|> Enum.map(&Ash.create!/1)
|
2022-06-22 13:00:47 +12:00
|
|
|
|
|
|
|
assert Enum.count(posts) == 5
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-06-03 00:18:02 +12:00
|
|
|
@meta_generator %{
|
2023-06-06 00:57:22 +12:00
|
|
|
meta: %{},
|
|
|
|
metadata: %{}
|
2023-06-03 00:18:02 +12:00
|
|
|
}
|
2022-06-22 13:00:47 +12:00
|
|
|
describe "seed_input" do
|
|
|
|
test "it returns attributes generated" do
|
|
|
|
Author
|
2023-06-03 00:18:02 +12:00
|
|
|
|> Ash.Generator.seed_input(@meta_generator)
|
2022-06-22 13:00:47 +12:00
|
|
|
|> Enum.take(10)
|
|
|
|
|> Enum.each(fn input ->
|
|
|
|
seed!(Author, input)
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "it can be used in property testing" do
|
2023-06-03 00:18:02 +12:00
|
|
|
check all(input <- Ash.Generator.seed_input(Author, @meta_generator)) do
|
2022-06-22 13:00:47 +12:00
|
|
|
seed!(Author, input)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
test "many seeds can be generated with seed_many!" do
|
2023-06-03 00:18:02 +12:00
|
|
|
assert Enum.count(Ash.Generator.seed_many!(Author, 5, @meta_generator)) == 5
|
2022-06-22 13:00:47 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "built in generators" do
|
2023-07-13 07:11:10 +12:00
|
|
|
for type <- Enum.uniq(Ash.Type.builtin_types()), type != Ash.Type.Keyword do
|
2022-06-22 13:00:47 +12:00
|
|
|
for type <- [{:array, type}, type] do
|
2023-07-13 07:11:10 +12:00
|
|
|
constraints =
|
|
|
|
case type do
|
|
|
|
{:array, type} ->
|
2024-03-28 09:06:40 +13:00
|
|
|
[items: Spark.Options.validate!([], Ash.Type.constraints(type))]
|
2023-07-13 07:11:10 +12:00
|
|
|
|
|
|
|
type ->
|
2024-03-28 09:06:40 +13:00
|
|
|
Spark.Options.validate!([], Ash.Type.constraints(type))
|
2023-07-13 07:11:10 +12:00
|
|
|
end
|
2022-06-22 13:00:47 +12:00
|
|
|
|
2023-10-27 15:04:35 +13:00
|
|
|
test "#{inspect(type)} type can be generated" do
|
|
|
|
try do
|
2022-06-22 13:00:47 +12:00
|
|
|
check all(input <- Ash.Type.generator(unquote(type), unquote(constraints))) do
|
|
|
|
{:ok, _} = Ash.Type.cast_input(unquote(type), input, unquote(constraints))
|
|
|
|
end
|
2023-10-27 15:04:35 +13:00
|
|
|
rescue
|
|
|
|
e in RuntimeError ->
|
|
|
|
case e do
|
|
|
|
%RuntimeError{message: "generator/1 unimplemented for" <> _} ->
|
|
|
|
:ok
|
|
|
|
|
|
|
|
other ->
|
|
|
|
reraise other, __STACKTRACE__
|
|
|
|
end
|
2022-06-22 13:00:47 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|