ash_graphql/test/relay_ids_test.exs
Riccardo Binetti 513c1ac68f
improvement!: port AshGraphql to Ash 3.0 (#123)
Step 1: update Ash

Step 2: mass rename Api to Domain

Step 3: Ash.Query.expr -> Ash.Expr.expr

Also change ref interpolation

Step 4: remove all warnings

Step 5: remove registries from tests

Step 6: fix filter

Step 7: private? -> !public?

Step 8: Ash.Calculation -> Ash.Resource.Calculation

Step 9: use depend_on_resources/1 -> resources/1

Step 10: add Domain to all resources

Step 11: use Ash module for all actions

Step 12: add public? true all around

Step 13: remove verbose? from options passed during Domain calls

Step 14: add simple_sat

Step 15: Ash.ErrorKind is no more, so remove code from errors

Step 16: sprinkle default_accept :* around tests

Step 17: replace Ash.Changeset.new/2 with Ash.Changeset.for_*

Step 18: calculation fixups

- Context is now a struct and arguments go under the arguments key
- Function based calculations receive a list of records
- Add a select to query-based loads
- select -> load

Step 19: pass the correct name to pass the policy in tests

Step 20: Ash.Query.new/2 is no more

Step 21: add AshGraphql.Resource.embedded? utility function

Use that instead of Ash.Type.embedded_type?(resource_or_type) since resources
are not types anymore

Step 22: handle struct + instance_of: Resource in unions

Resources are not type anymore so they need to be passed this way in unions

Step 23: ensure we only check GraphQL actions for pagination

All reads are now paginated by default, so this triggered a compilation error

Step 24: swap arguments for sort on calculations

Step 25: remove unused debug? option
2024-04-01 14:03:06 -04:00

532 lines
12 KiB
Elixir

defmodule AshGraphql.RelayIdsTest do
use ExUnit.Case, async: false
alias AshGraphql.Test.RelayIds.{Post, ResourceWithNoPrimaryKeyGet, Schema, User}
setup do
on_exit(fn ->
AshGraphql.TestHelpers.stop_ets()
end)
end
describe "relay global ID" do
test "can be used in get queries and is exposed correctly in relationships" do
user =
User
|> Ash.Changeset.for_create(:create, %{name: "fred"})
|> Ash.create!()
post =
Post
|> Ash.Changeset.for_create(
:create,
%{
author_id: user.id,
text: "foo"
}
)
|> Ash.create!()
user_relay_id = AshGraphql.Resource.encode_relay_id(user)
post_relay_id = AshGraphql.Resource.encode_relay_id(post)
resp =
"""
query GetPost($id: ID!) {
getPost(id: $id) {
text
author {
id
name
}
}
}
"""
|> Absinthe.run(Schema,
variables: %{
"id" => post_relay_id
}
)
assert {:ok, result} = resp
refute Map.has_key?(result, :errors)
assert %{
data: %{
"getPost" => %{
"text" => "foo",
"author" => %{"id" => ^user_relay_id, "name" => "fred"}
}
}
} = result
end
test "returns error on invalid ID" do
resp =
"""
query GetPost($id: ID!) {
getPost(id: $id) {
text
}
}
"""
|> Absinthe.run(Schema,
variables: %{
"id" => "invalid"
}
)
assert {:ok, result} = resp
assert [%{message: "invalid primary key provided"}] = result[:errors]
end
test "returns error on ID for wrong resource" do
user =
User
|> Ash.Changeset.for_create(:create, %{name: "fred"})
|> Ash.create!()
user_relay_id = AshGraphql.Resource.encode_relay_id(user)
resp =
"""
query GetPost($id: ID!) {
getPost(id: $id) {
text
}
}
"""
|> Absinthe.run(Schema,
variables: %{
"id" => user_relay_id
}
)
assert {:ok, result} = resp
assert [%{message: "invalid primary key provided"}] = result[:errors]
end
end
describe "node interface and query" do
test "allows retrieving resources" do
user =
User
|> Ash.Changeset.for_create(:create, %{name: "fred"})
|> Ash.create!()
post =
Post
|> Ash.Changeset.for_create(
:create,
%{
author_id: user.id,
text: "foo"
}
)
|> Ash.create!()
user_relay_id = AshGraphql.Resource.encode_relay_id(user)
post_relay_id = AshGraphql.Resource.encode_relay_id(post)
document =
"""
query Node($id: ID!) {
node(id: $id) {
__typename
... on User {
name
}
... on Post {
text
}
}
}
"""
resp =
document
|> Absinthe.run(Schema,
variables: %{
"id" => post_relay_id
}
)
assert {:ok, result} = resp
refute Map.has_key?(result, :errors)
assert %{
data: %{
"node" => %{
"__typename" => "Post",
"text" => "foo"
}
}
} = result
resp =
document
|> Absinthe.run(Schema,
variables: %{
"id" => user_relay_id
}
)
assert {:ok, result} = resp
refute Map.has_key?(result, :errors)
assert %{
data: %{
"node" => %{
"__typename" => "User",
"name" => "fred"
}
}
} = result
end
test "return an error for resources without a primary key get" do
resource =
ResourceWithNoPrimaryKeyGet
|> Ash.Changeset.for_create(:create, %{name: "foo"})
|> Ash.create!()
document =
"""
query Node($id: ID!) {
node(id: $id) {
__typename
... on ResourceWithNoPrimaryKeyGet{
name
}
}
}
"""
resource_relay_id = AshGraphql.Resource.encode_relay_id(resource)
resp =
document
|> Absinthe.run(Schema,
variables: %{
"id" => resource_relay_id
}
)
assert {:ok, result} = resp
assert result[:errors] != nil
end
end
describe "relay ID decoding" do
test "round trips" do
user =
User
|> Ash.Changeset.for_create(:create, %{name: "Fred"})
|> Ash.create!()
user_id = user.id
user_type = AshGraphql.Resource.Info.type(User)
user_relay_id = AshGraphql.Resource.encode_relay_id(user)
assert {:ok, %{type: ^user_type, id: ^user_id}} =
AshGraphql.Resource.decode_relay_id(user_relay_id)
end
test "fails for invalid ids" do
assert {:error, %Ash.Error.Invalid.InvalidPrimaryKey{}} =
AshGraphql.Resource.decode_relay_id("notbase64")
assert {:error, %Ash.Error.Invalid.InvalidPrimaryKey{}} =
"non-existing-type:1234"
|> Base.encode64()
|> AshGraphql.Resource.decode_relay_id()
assert {:error, %Ash.Error.Invalid.InvalidPrimaryKey{}} =
"user"
|> Base.encode64()
|> AshGraphql.Resource.decode_relay_id()
end
end
describe "relay ID translation" do
test "works with create mutations" do
author_id =
User
|> Ash.Changeset.for_create(:create, %{name: "Fred"})
|> Ash.create!()
|> AshGraphql.Resource.encode_relay_id()
resp =
"""
mutation SimpleCreatePost($input: SimpleCreatePostInput) {
simpleCreatePost(input: $input) {
result {
text
author {
id
}
}
errors {
message
}
}
}
"""
|> Absinthe.run(Schema,
variables: %{
"input" => %{
"text" => "foo",
"author_id" => author_id
}
}
)
assert {:ok, result} = resp
refute Map.has_key?(result, :errors)
assert %{
data: %{
"simpleCreatePost" => %{
"result" => %{
"text" => "foo",
"author" => %{
"id" => ^author_id
}
}
}
}
} = result
end
test "works in update mutations" do
author_id =
User
|> Ash.Changeset.for_create(:create, %{name: "Fred"})
|> Ash.create!()
|> AshGraphql.Resource.encode_relay_id()
post_id =
Post
|> Ash.Changeset.for_create(:create, %{text: "foo"})
|> Ash.create!()
|> AshGraphql.Resource.encode_relay_id()
resp =
"""
mutation AssignAuthor($id: ID!, $input: AssignAuthorInput) {
assignAuthor(id: $id, input: $input) {
result {
text
author {
id
}
}
errors {
message
}
}
}
"""
|> Absinthe.run(Schema,
variables: %{
"id" => post_id,
"input" => %{
"author_id" => author_id
}
}
)
assert {:ok, result} = resp
refute Map.has_key?(result, :errors)
assert %{
data: %{
"assignAuthor" => %{
"result" => %{
"author" => %{
"id" => ^author_id
}
}
}
}
} = result
end
test "works with lists" do
author_id =
User
|> Ash.Changeset.for_create(:create, %{name: "Fred"})
|> Ash.create!()
|> AshGraphql.Resource.encode_relay_id()
post_ids =
Enum.map(1..5, fn i ->
Post
|> Ash.Changeset.for_create(:create, %{text: "foo #{i}"})
|> Ash.create!()
|> AshGraphql.Resource.encode_relay_id()
end)
resp =
"""
mutation AssignPosts($id: ID!, $input: AssignPostsInput) {
assignPosts(id: $id, input: $input) {
result {
posts {
id
}
}
errors {
message
}
}
}
"""
|> Absinthe.run(Schema,
variables: %{
"id" => author_id,
"input" => %{
"post_ids" => post_ids
}
}
)
assert {:ok, result} = resp
refute Map.has_key?(result, :errors)
assert %{
data: %{
"assignPosts" => %{
"result" => %{
"posts" => posts
}
}
}
} = result
assert length(posts) == 5
Enum.each(posts, fn post -> assert post["id"] in post_ids end)
end
test "rejects invalid IDs" do
author_id =
User
|> Ash.Changeset.for_create(:create, %{name: "Fred"})
|> Ash.create!()
|> AshGraphql.Resource.encode_relay_id()
post_ids =
Enum.map(1..5, fn i ->
Post
|> Ash.Changeset.for_create(:create, %{text: "foo #{i}"})
|> Ash.create!()
|> AshGraphql.Resource.encode_relay_id()
end)
post_ids = ["invalid_id" | post_ids]
resp =
"""
mutation AssignPosts($id: ID!, $input: AssignPostsInput) {
assignPosts(id: $id, input: $input) {
result {
posts {
id
}
}
errors {
fields
message
}
}
}
"""
|> Absinthe.run(Schema,
variables: %{
"id" => author_id,
"input" => %{
"post_ids" => post_ids
}
}
)
assert {:ok, result} = resp
assert %{
data: %{
"assignPosts" => %{
"result" => nil,
"errors" => [
%{
"fields" => ["post_ids"],
"message" => "is invalid"
}
]
}
}
} = result
end
test "rejects IDs for another type" do
author_id =
User
|> Ash.Changeset.for_create(:create, %{name: "Fred"})
|> Ash.create!()
|> AshGraphql.Resource.encode_relay_id()
post_ids = [author_id]
resp =
"""
mutation AssignPosts($id: ID!, $input: AssignPostsInput) {
assignPosts(id: $id, input: $input) {
result {
posts {
id
}
}
errors {
fields
message
}
}
}
"""
|> Absinthe.run(Schema,
variables: %{
"id" => author_id,
"input" => %{
"post_ids" => post_ids
}
}
)
assert {:ok, result} = resp
assert %{
data: %{
"assignPosts" => %{
"result" => nil,
"errors" => [
%{
"fields" => ["post_ids"],
"message" => "is invalid"
}
]
}
}
} = result
end
end
end