Skip to content

Commit

Permalink
add some policy tests for subscriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
barnabasJ committed Sep 24, 2024
1 parent d324eef commit 76d4c86
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 25 deletions.
2 changes: 1 addition & 1 deletion lib/subscription/actor_function.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule AshGraphql.Subscription.ActorFunction do
@moduledoc false

@behaviour AshGraphql.Resource.Subscription.Actor
@behaviour AshGraphql.Subscription.Actor

@impl true
def actor(actor, [{:fun, {m, f, a}}]) do
Expand Down
194 changes: 170 additions & 24 deletions test/subscription_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,54 @@ defmodule AshGraphql.SubscriptionTest do

alias AshGraphql.Test.PubSub
alias AshGraphql.Test.Schema
alias AshGraphql.Test.Subscribable

def assert_down(pid) do
ref = Process.monitor(pid)

assert_receive {:DOWN, ^ref, _, _, _}
end

setup do
Application.put_env(PubSub, :notifier_test_pid, self())
{:ok, _} = PubSub.start_link()
{:ok, _} = Absinthe.Subscription.start_link(PubSub)
{:ok, pubsub} = PubSub.start_link()
{:ok, absinthe_sub} = Absinthe.Subscription.start_link(PubSub)
:ok

on_exit(fn ->
Process.exit(pubsub, :normal)
Process.exit(absinthe_sub, :normal)
# block until the processes have exited
assert_down(pubsub)
assert_down(absinthe_sub)
end)
end

@query """
subscription {
subscribableEvents {
created {
id
text
}
updated {
id
text
}
destroyed
}
@admin %{
id: 1,
role: :admin
}
"""
@tag :wip
test "can subscribe to a resource" do
id = "1"

test "can subscribe to all action types resource" do
assert {:ok, %{"subscribed" => topic}} =
Absinthe.run(
@query,
"""
subscription {
subscribableEvents {
created {
id
text
}
updated {
id
text
}
destroyed
}
}
""",
Schema,
context: %{actor: %{id: id}, pubsub: PubSub}
context: %{actor: @admin, pubsub: PubSub}
)

create_mutation = """
Expand All @@ -52,7 +68,10 @@ defmodule AshGraphql.SubscriptionTest do
"""

assert {:ok, %{data: mutation_result}} =
Absinthe.run(create_mutation, Schema, variables: %{"input" => %{"text" => "foo"}})
Absinthe.run(create_mutation, Schema,
variables: %{"input" => %{"text" => "foo"}},
context: %{actor: @admin}
)

assert Enum.empty?(mutation_result["createSubscribable"]["errors"])

Expand Down Expand Up @@ -80,7 +99,8 @@ defmodule AshGraphql.SubscriptionTest do

assert {:ok, %{data: mutation_result}} =
Absinthe.run(update_mutation, Schema,
variables: %{"id" => subscribable_id, "input" => %{"text" => "bar"}}
variables: %{"id" => subscribable_id, "input" => %{"text" => "bar"}},
context: %{actor: @admin}
)

assert Enum.empty?(mutation_result["updateSubscribable"]["errors"])
Expand All @@ -103,12 +123,138 @@ defmodule AshGraphql.SubscriptionTest do
"""

assert {:ok, %{data: mutation_result}} =
Absinthe.run(destroy_mutation, Schema, variables: %{"id" => subscribable_id})
Absinthe.run(destroy_mutation, Schema,
variables: %{"id" => subscribable_id},
context: %{actor: @admin}
)

assert Enum.empty?(mutation_result["destroySubscribable"]["errors"])

assert_receive({^topic, %{data: subscription_data}})

assert subscription_data["subscribableEvents"]["destroyed"] == subscribable_id
end

test "policies are applied to subscriptions" do
actor1 = %{
id: 1,
role: :user
}

actor2 = %{
id: 2,
role: :user
}

assert {:ok, %{"subscribed" => topic1}} =
Absinthe.run(
"""
subscription {
subscribableEvents {
created {
id
text
}
updated {
id
text
}
destroyed
}
}
""",
Schema,
context: %{actor: actor1, pubsub: PubSub}
)

assert {:ok, %{"subscribed" => topic2}} =
Absinthe.run(
"""
subscription {
subscribableEvents {
created {
id
text
}
updated {
id
text
}
destroyed
}
}
""",
Schema,
context: %{actor: actor2, pubsub: PubSub}
)

assert topic1 != topic2

subscribable =
Subscribable
|> Ash.Changeset.for_create(:create, %{text: "foo", actor_id: 1}, actor: @admin)
|> Ash.create!()

# actor1 will get data because it can see the resource
assert_receive {^topic1, %{data: subscription_data}}
# actor 2 will not get data because it cannot see the resource
refute_receive({^topic2, _})

assert subscribable.id ==
subscription_data["subscribableEvents"]["created"]["id"]
end

test "can dedup with actor fun" do
actor1 = %{
id: 1,
role: :user
}

actor2 = %{
id: 2,
role: :user
}

subscription = """
subscription {
dedupedSubscribableEvents {
created {
id
text
}
updated {
id
text
}
destroyed
}
}
"""

assert {:ok, %{"subscribed" => topic1}} =
Absinthe.run(
subscription,
Schema,
context: %{actor: actor1, pubsub: PubSub}
)

assert {:ok, %{"subscribed" => topic2}} =
Absinthe.run(
subscription,
Schema,
context: %{actor: actor2, pubsub: PubSub}
)

assert topic1 == topic2

subscribable =
Subscribable
|> Ash.Changeset.for_create(:create, %{text: "foo", actor_id: 1}, actor: @admin)
|> Ash.create!()

assert_receive {^topic1, %{data: subscription_data}}

assert subscribable.id ==
subscription_data["dedupedSubscribableEvents"]["created"]["id"]
end
end
27 changes: 27 additions & 0 deletions test/support/resources/subscribable.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ defmodule AshGraphql.Test.Subscribable do
use Ash.Resource,
domain: AshGraphql.Test.Domain,
data_layer: Ash.DataLayer.Ets,
authorizers: [Ash.Policy.Authorizer],
extensions: [AshGraphql.Resource]

require Ash.Query
Expand All @@ -26,18 +27,44 @@ defmodule AshGraphql.Test.Subscribable do
subscribe(:subscribable_events) do
actions([:create, :update, :destroy])
end

subscribe(:deduped_subscribable_events) do
actions([:create, :update, :destroy])
read_action(:open_read)

actor(fn _ ->
%{id: -1, role: :deduped_actor}
end)
end
end
end

policies do
bypass actor_attribute_equals(:role, :admin) do
authorize_if(always())
end

policy action(:read) do
authorize_if(expr(actor_id == ^actor(:id)))
end

policy action(:open_read) do
authorize_if(always())
end
end

actions do
default_accept(:*)
defaults([:create, :read, :update, :destroy])

read(:open_read)
end

attributes do
uuid_primary_key(:id)

attribute(:text, :string, public?: true)
attribute(:actor_id, :integer, public?: true)
create_timestamp(:created_at)
update_timestamp(:updated_at)
end
Expand Down

0 comments on commit 76d4c86

Please sign in to comment.