Skip to content

Commit

Permalink
feat(Absinthe.Phoenix.SubscriptionTest): Add easy to use test helpers…
Browse files Browse the repository at this point in the history
… for testing subscriptions
  • Loading branch information
Jacek Tomaszewski committed Nov 16, 2022
1 parent f1436bf commit e0090e0
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 9 deletions.
154 changes: 146 additions & 8 deletions lib/absinthe/phoenix/subscription_test.ex
Original file line number Diff line number Diff line change
@@ -1,9 +1,37 @@
defmodule Absinthe.Phoenix.SubscriptionTest do
@moduledoc """
Convenience functions for subscription tests.
## Example
defmodule MyApp.SocketTest do
use ExUnit.Case
import Absinthe.Phoenix.SubscriptionTest
test "once unread thread is created, unread threads count changes subscription gets an update" do
socket = build_socket(MyApp.Endpoint, MyApp.Socket)
subscription_id =
start_subscription(socket, \"""
subscription {
unreadThreadsCountChanges {
count
}
}
\""")
create_unread_thread()
assert_subscription_update(subscription_id, %{
"unreadThreadsCountChanges" => %{"count" => 1}
})
refute_subscription_update(subscription_id)
end
end
"""

@typep opts ::
@typep push_doc_opts ::
[variables: Access.container()]
| %{variables: Access.container()}

Expand Down Expand Up @@ -32,20 +60,130 @@ defmodule Absinthe.Phoenix.SubscriptionTest do
end
end

@doc ~S"""
Build a socket connection to a `Absinthe.Phoenix.Socket` using the given `Absinthe.Phoenix.Endpoint`.
Optionally pass `socket_params` which will will be passed to the `Absinthe.Phoenix.Socket.connect` function.
## Example
AL.MobileApi.AbsinthePhoenixSocketTest.build_socket(
MyApp.Endpoint,
MyApp.Socket,
%{
"Authorization" => "Bearer #{auth_token}"
}
)
"""
@spec build_socket(term(), term(), map()) :: reference()
def build_socket(endpoint, socket_module, socket_params \\ %{}) do
{:ok, socket} = Phoenix.ChannelTest.__connect__(endpoint, socket_module, socket_params, %{})
{:ok, socket} = join_absinthe(socket)
socket
end

@doc """
Start a GraphQL subscription using the given `socket` connection (built using `build_socket`).
Returns `subscription_id`.
## Example
subscription_id =
start_subscription(socket, \"""
subscription {
unreadThreadsCountChanges {
count
}
}
\""")
"""
@spec start_subscription(reference(), String.t(), keyword()) :: String.t()
defmacro start_subscription(
socket,
query,
opts \\ []
) do
quote do
opts = unquote(opts)
variables = Keyword.get(opts, :variables, %{})
timeout = Keyword.get(opts, :timeout, Application.fetch_env!(:ex_unit, :assert_receive_timeout))

ref = Absinthe.Phoenix.SubscriptionTest.push_doc(unquote(socket), unquote(query), variables)

Phoenix.ChannelTest.assert_reply(
ref,
:ok,
%{subscriptionId: subscription_id},
timeout
)

subscription_id
end
end

@doc """
Assert that the GraphQL subscription has received data update.
## Example
assert_subscription_update(subscription_id, %{
"unreadThreadsCountChanges" => %{"count" => 1}
})
"""
@spec assert_subscription_update(String.t(), map(), integer()) :: nil
defmacro assert_subscription_update(
subscription_id,
data,
timeout \\ Application.fetch_env!(:ex_unit, :assert_receive_timeout)
) do
quote do
Phoenix.ChannelTest.assert_push(
"subscription:data",
%{
subscriptionId: ^unquote(subscription_id),
result: %{
data: unquote(data)
}
},
unquote(timeout)
)
end
end

@doc """
Assert that the GraphQL subscription has received no data updates.
## Example
refute_subscription_update(subscription_id)
"""
@spec refute_subscription_update(String.t(), integer()) :: nil
defmacro refute_subscription_update(
subscription_id,
timeout \\ Application.fetch_env!(:ex_unit, :assert_receive_timeout)
) do
quote do
Phoenix.ChannelTest.refute_push(
"subscription:data",
%{
subscriptionId: ^unquote(subscription_id)
},
unquote(timeout)
)
end
end

@doc false
def join_absinthe(socket) do
with {:ok, _, socket} <-
Phoenix.ChannelTest.subscribe_and_join(socket, "__absinthe__:control", %{}) do
{:ok, socket}
end
end

@doc """
A small wrapper around `Phoenix.ChannelTest.push/3`.
The only option that is used is `opts[:variables]` - all other options are
ignored.
"""
@spec push_doc(Phoenix.Socket.t(), String.t(), opts) :: reference()
@doc false
@spec push_doc(Phoenix.Socket.t(), String.t(), push_doc_opts) :: reference()
def push_doc(socket, query, opts \\ []) do
Phoenix.ChannelTest.push(socket, "doc", %{
"query" => query,
Expand Down
41 changes: 41 additions & 0 deletions test/absinthe/phoenix/subscription_test_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
defmodule Absinthe.Phoenix.SubscriptionTestTest do
use ExUnit.Case
use Absinthe.Phoenix.ChannelCase
import Absinthe.Phoenix.SubscriptionTest

setup_all do
Absinthe.Test.prime(Schema)

children = [
{Phoenix.PubSub, [name: Absinthe.Phoenix.PubSub, adapter: Phoenix.PubSub.PG2]},
Absinthe.Phoenix.TestEndpoint,
{Absinthe.Subscription, Absinthe.Phoenix.TestEndpoint}
]

{:ok, _} = Supervisor.start_link(children, strategy: :one_for_one)
:ok
end

test "subscription can be started and its' updates can be asserted" do
socket = build_socket(Absinthe.Phoenix.TestEndpoint, Absinthe.Phoenix.TestSocket)

subscription_id = start_subscription(socket, "subscription {commentAdded { contents }}")

ref =
push(socket, "doc", %{
"query" => "mutation ($contents: String!) {addComment(contents: $contents) { contents }}",
"variables" => %{"contents" => "hello world"}
})

assert_reply(ref, :ok, reply)

expected = %{data: %{"addComment" => %{"contents" => "hello world"}}}
assert expected == reply

assert_subscription_update(subscription_id, %{
"commentAdded" => %{"contents" => "hello world"}
})

refute_subscription_update(subscription_id)
end
end
2 changes: 1 addition & 1 deletion test/support/test_socket.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule Absinthe.Phoenix.TestSocket do
use Phoenix.Socket
use Absinthe.Phoenix.Socket
use Absinthe.Phoenix.Socket, schema: Schema

def connect(_, socket, _connect_info) do
{:ok, socket}
Expand Down

0 comments on commit e0090e0

Please sign in to comment.