diff --git a/lib/poxa.ex b/lib/poxa.ex index 8d272ed..9f8b6c2 100644 --- a/lib/poxa.ex +++ b/lib/poxa.ex @@ -9,7 +9,7 @@ defmodule Poxa do def start(_type, _args) do dispatch = :cowboy_router.compile([ {:_, [ { '/ping', Poxa.PingHandler, [] }, - { '/console', Poxa.Console.WSHandler, [] }, + { '/apps/:app_id/console', Poxa.Console.WSHandler, [] }, { '/', :cowboy_static, {:priv_file, :poxa, 'index.html'} }, { '/static/[...]', :cowboy_static, {:priv_dir, :poxa, 'static'} }, { '/apps/:app_id/events', Poxa.EventHandler, [] }, diff --git a/lib/poxa/app.ex b/lib/poxa/app.ex new file mode 100644 index 0000000..4513233 --- /dev/null +++ b/lib/poxa/app.ex @@ -0,0 +1,89 @@ +defmodule Poxa.App do + use GenServer + + @doc """ + Given the app_key it returns the related app_id + """ + def id(key) do + case :ets.match(Poxa.App, {:'$1', key, :'_'}) do + [[id]] -> {:ok, id} + _ -> {:error, {:key_not_found, key}} + end + end + + @doc """ + Given the app_id it returns the related app_key + """ + @spec key(binary) :: {:ok, binary} | {:error, term} + def key(id) do + case :ets.lookup(Poxa.App, id) do + [{^id, key, _}] -> {:ok, key} + _ -> {:error, {:id_not_found, id}} + end + end + + @doc """ + Given the app_key it returns the related app_secret + """ + def secret(key) do + case :ets.match(Poxa.App, {:'_', key, :'$1'}) do + [[secret]] -> {:ok, secret} + _ -> {:error, {:key_not_found, key}} + end + end + + @doc """ + Given the app_id it returns the related app_key and secret + """ + def key_secret(id) do + case :ets.lookup(Poxa.App, id) do + [{^id, key, secret}] -> {:ok, {key, secret}} + _ -> {:error, {:id_not_found, id}} + end + end + + @doc """ + Reload table content using Application environment + """ + @spec reload :: :ok + def reload do + GenServer.call(Poxa.App, :reload) + end + + @doc false + def start_link(args \\ []) do + GenServer.start_link(__MODULE__, args, [name: Poxa.App]) + end + + @doc """ + Create ETS table to fetch app_id, app_key and secret + """ + def init(_) do + ets_options = [:ordered_set, :named_table, :protected, { :read_concurrency, true }] + :ets.new(Poxa.App, ets_options) + populate_table + { :ok, :ok } + end + + def handle_call(:reload, _from, _) do + populate_table + {:reply, :ok, :ok} + end + + defp populate_table do + apps = Application.get_env(:poxa, :apps, []) + app = fetch_standalone_app + for {app_id, app_key, secret} <- [app | apps] do + :ets.insert(Poxa.App, {app_id, app_key, secret}) + end + end + + defp fetch_standalone_app do + app_id = Application.get_env(:poxa, :app_id) + app_key = Application.get_env(:poxa, :app_key) + app_secret = Application.get_env(:poxa, :app_secret) + if app_id && app_key && app_secret do + {app_id, app_key, app_secret} + end + end +end diff --git a/lib/poxa/auth_signature.ex b/lib/poxa/auth_signature.ex index 74c98cf..e100e82 100644 --- a/lib/poxa/auth_signature.ex +++ b/lib/poxa/auth_signature.ex @@ -4,25 +4,21 @@ defmodule Poxa.AuthSignature do http://pusher.com/docs/auth_signatures """ - require Logger - alias Poxa.CryptoHelper - @doc """ Validate auth signature as described at: http://pusher.com/docs/auth_signatures . Returns true or false """ - @spec valid?(binary, binary) :: boolean - def valid?(to_sign, auth) do - case String.split(auth, ":", parts: 2) do - [app_key, remote_signed_data] -> - signed_data = sign_data(to_sign) - Poxa.Authentication.check_key(app_key) and signed_data == remote_signed_data - _ -> false + @spec valid?(binary, binary, binary) :: boolean + def valid?(app_id, to_sign, auth) do + case Poxa.App.key_secret(app_id) do + {:ok, {app_key, app_secret}} -> + case String.split(auth, ":", parts: 2) do + [^app_key, remote_signed_data] -> + signed_data = Poxa.CryptoHelper.hmac256_to_string(app_secret, to_sign) + signed_data == remote_signed_data + _ -> false + end + {:error, _} -> false end end - - defp sign_data(data) do - {:ok, app_secret} = :application.get_env(:poxa, :app_secret) - CryptoHelper.hmac256_to_string(app_secret, data) - end end diff --git a/lib/poxa/authentication.ex b/lib/poxa/authentication.ex index f2858f4..4561233 100644 --- a/lib/poxa/authentication.ex +++ b/lib/poxa/authentication.ex @@ -7,23 +7,15 @@ defmodule Poxa.Authentication do Returns true if authentication process is validated correctly, false otherwise More info at: http://pusher.com/docs/rest_api#authentication """ - @spec check(binary, binary, binary, [{binary, binary}]) :: boolean - def check(method, path, body, qs_vals) do + @spec check(binary, binary, binary, binary, [{binary, binary}]) :: boolean + def check(app_id, method, path, body, qs_vals) do qs_vals = Enum.into(qs_vals, %{}) body_md5 = qs_vals["body_md5"] - {:ok, app_key} = Application.fetch_env(:poxa, :app_key) - {:ok, secret} = Application.fetch_env(:poxa, :app_secret) - - valid_body?(body, body_md5) and Signaturex.validate(app_key, secret, method, path, qs_vals, 600) - end - - @doc """ - Returns true if the `app_key` is the same as `auth_key`, false otherwise - """ - @spec check_key(binary) :: boolean - def check_key(auth_key) do - {:ok, app_key} = Application.fetch_env(:poxa, :app_key) - auth_key == app_key + case Poxa.App.key_secret(app_id) do + {:ok, {app_key, secret}} -> + valid_body?(body, body_md5) and Signaturex.validate(app_key, secret, method, path, qs_vals, 600) + {:error, _} -> false + end end defp valid_body?("", nil), do: true diff --git a/lib/poxa/authorization_helper.ex b/lib/poxa/authorization_helper.ex index d135fc3..0b98d6b 100644 --- a/lib/poxa/authorization_helper.ex +++ b/lib/poxa/authorization_helper.ex @@ -1,13 +1,19 @@ defmodule Poxa.AuthorizationHelper do - alias Poxa.Authentication + @moduledoc """ + Helper to keep the cowboy rest is_authorized dry for Pusher authentication + """ + @doc """ + A helper to share authentication process between handlers. It uses the cowboy `req` to retrieve the necessary data. + """ @spec is_authorized(:cowboy_req.req, any) :: {true, :cowboy_req.req, any} | {{false, binary}, :cowboy_req.req, nil} def is_authorized(req, state) do {:ok, body, req} = :cowboy_req.body(req) {method, req} = :cowboy_req.method(req) {qs_vals, req} = :cowboy_req.qs_vals(req) {path, req} = :cowboy_req.path(req) - if Authentication.check(method, path, body, qs_vals) do + {app_id, req} = :cowboy_req.binding(:app_id, req) + if Poxa.Authentication.check(app_id, method, path, body, qs_vals) do {true, req, state} else {{false, "authentication failed"}, req, nil} diff --git a/lib/poxa/channel.ex b/lib/poxa/channel.ex index 46cd807..89083be 100644 --- a/lib/poxa/channel.ex +++ b/lib/poxa/channel.ex @@ -86,36 +86,36 @@ defmodule Poxa.Channel do @doc """ Returns true if the channel has at least 1 subscription """ - @spec occupied?(binary) :: boolean - def occupied?(channel) do - match = {{:p, :l, {:pusher, channel}}, :_, :_} + @spec occupied?(binary, binary) :: boolean + def occupied?(channel, app_id) do + match = {{:p, :l, {:pusher, app_id, channel}}, :_, :_} :gproc.select_count([{match, [], [true]}]) != 0 end @doc """ Returns the list of channels the `pid` is subscribed """ - @spec all(pid | :_) :: [binary] - def all(pid \\ :_) do - match = {{:p, :l, {:pusher, :'$1'}}, pid, :_} + @spec all(binary, pid | :_) :: [binary] + def all(app_id, pid \\ :_) do + match = {{:p, :l, {:pusher, app_id, :'$1'}}, pid, :_} :gproc.select([{match, [], [:'$1']}]) |> Enum.uniq end @doc """ Returns true if `pid` is subscribed to `channel` and false otherwise """ - @spec subscribed?(binary, pid) :: boolean - def subscribed?(channel, pid) do - match = {{:p, :l, {:pusher, channel}}, pid, :_} + @spec subscribed?(binary, binary, pid) :: boolean + def subscribed?(channel, app_id, pid) do + match = {{:p, :l, {:pusher, app_id, channel}}, pid, :_} :gproc.select_count([{match, [], [true]}]) != 0 end @doc """ Returns how many connections are opened on the `channel` """ - @spec subscription_count(binary) :: non_neg_integer - def subscription_count(channel) do - match = {{:p, :l, {:pusher, channel}}, :_, :_} + @spec subscription_count(binary, binary) :: non_neg_integer + def subscription_count(channel, app_id) do + match = {{:p, :l, {:pusher, app_id, channel}}, :_, :_} :gproc.select_count([{match, [], [true]}]) end end diff --git a/lib/poxa/channels_handler.ex b/lib/poxa/channels_handler.ex index 2212a2d..8f8519c 100644 --- a/lib/poxa/channels_handler.ex +++ b/lib/poxa/channels_handler.ex @@ -32,11 +32,12 @@ defmodule Poxa.ChannelsHandler do attributes = String.split(info, ",") |> Enum.reject(&(&1 == "")) {channel, req} = :cowboy_req.binding(:channel_name, req, nil) + {app_id, req} = :cowboy_req.binding(:app_id, req) if channel do - {malformed_request_one_channel?(attributes, channel), req, {:one, channel, attributes}} + {malformed_request_one_channel?(attributes, channel), req, {:one, app_id, channel, attributes}} else {filter, req} = :cowboy_req.qs_val("filter_by_prefix", req, nil) - {malformed_request_all_channels?(attributes, filter), req, {:all, filter, attributes}} + {malformed_request_all_channels?(attributes, filter), req, {:all, app_id, filter, attributes}} end end @@ -71,42 +72,42 @@ defmodule Poxa.ChannelsHandler do end @doc false - def get_json(req, {:one, channel, attributes}) do - show(channel, attributes, req, nil) + def get_json(req, {:one, app_id, channel, attributes}) do + show(app_id, channel, attributes, req, nil) end - def get_json(req, {:all, filter, attributes}) do - index(filter, attributes, req, nil) + def get_json(req, {:all, app_id, filter, attributes}) do + index(app_id, filter, attributes, req, nil) end - defp show(channel, attributes, req, state) do - occupied = Channel.occupied?(channel) - attribute_list = mount_attribute_list(attributes, channel) + defp show(app_id, channel, attributes, req, state) do + occupied = Channel.occupied?(channel, app_id) + attribute_list = mount_attribute_list(attributes, app_id, channel) {JSX.encode!([occupied: occupied] ++ attribute_list), req, state} end - defp mount_attribute_list(attributes, channel) do + defp mount_attribute_list(attributes, app_id, channel) do attribute_list = if Enum.member?(attributes, "subscription_count") do - [subscription_count: Channel.subscription_count(channel)] + [subscription_count: Channel.subscription_count(app_id, channel)] else [] end attribute_list ++ if Enum.member?(attributes, "user_count") do - [user_count: PresenceChannel.user_count(channel)] + [user_count: PresenceChannel.user_count(app_id, channel)] else [] end end - defp index(filter, attributes, req, state) do - channels = channels(filter, attributes) + defp index(app_id, filter, attributes, req, state) do + channels = channels(app_id, filter, attributes) {JSX.encode!(channels: channels), req, state} end - defp channels(filter, attributes) do - for channel <- Channel.all, filter_channel(channel, filter) do - {channel, mount_attribute_list(attributes, channel)} + defp channels(app_id, filter, attributes) do + for channel <- Channel.all(app_id), filter_channel(channel, filter) do + {channel, mount_attribute_list(attributes, app_id, channel)} end end diff --git a/lib/poxa/console/console.ex b/lib/poxa/console/console.ex index 91ad14c..f73068d 100644 --- a/lib/poxa/console/console.ex +++ b/lib/poxa/console/console.ex @@ -3,39 +3,41 @@ defmodule Poxa.Console do use GenEvent @doc false - def init(pid), do: {:ok, pid} + def init([pid, app_id]), do: {:ok, {pid, app_id}} @doc false - def handle_event(%{event: :connected, socket_id: socket_id, origin: origin}, pid) do + def handle_event(%{event: :connected, app_id: app_id, socket_id: socket_id, origin: origin}, {pid, app_id}) do send_message("Connection", socket_id, "Origin: #{origin}", pid) - {:ok, pid} + {:ok, {pid, app_id}} end - def handle_event(%{event: :disconnected, socket_id: socket_id, channels: channels, duration: duration}, pid) do + def handle_event(%{event: :disconnected, app_id: app_id, socket_id: socket_id, channels: channels, duration: duration}, {pid, app_id}) do send_message("Disconnection", socket_id, "Channels: #{inspect channels}, Lifetime: #{duration}s", pid) - {:ok, pid} + {:ok, {pid, app_id}} end - def handle_event(%{event: :subscribed, socket_id: socket_id, channel: channel}, pid) do + def handle_event(%{event: :subscribed, app_id: app_id, socket_id: socket_id, channel: channel}, {pid, app_id}) do send_message("Subscribed", socket_id, "Channel: #{channel}", pid) - {:ok, pid} + {:ok, {pid, app_id}} end - def handle_event(%{event: :unsubscribed, socket_id: socket_id, channel: channel}, pid) do + def handle_event(%{event: :unsubscribed, app_id: app_id, socket_id: socket_id, channel: channel}, {pid, app_id}) do send_message("Unsubscribed", socket_id, "Channel: #{channel}", pid) - {:ok, pid} + {:ok, {pid, app_id}} end - def handle_event(%{event: :api_message, channels: channels, name: name}, pid) do + def handle_event(%{event: :api_message, app_id: app_id, channels: channels, name: name}, {pid, app_id}) do send_message("API Message", "", "Channels: #{inspect channels}, Event: #{name}", pid) - {:ok, pid} + {:ok, {pid, app_id}} end - def handle_event(%{event: :client_event_message, socket_id: socket_id, channels: channels, name: name}, pid) do + def handle_event(%{event: :client_event_message, app_id: app_id, socket_id: socket_id, channels: channels, name: name}, {pid, app_id}) do send_message("Client Event Message", socket_id, "Channels: #{inspect channels}, Event: #{name}", pid) - {:ok, pid} + {:ok, {pid, app_id}} end + def handle_event(_data, state), do: {:ok, state} + defp send_message(type, socket_id, details, pid) do msg = message(type, socket_id, details) |> encode! send(pid, msg) diff --git a/lib/poxa/console/ws_handler.ex b/lib/poxa/console/ws_handler.ex index fbab025..b82b7d0 100644 --- a/lib/poxa/console/ws_handler.ex +++ b/lib/poxa/console/ws_handler.ex @@ -11,12 +11,13 @@ defmodule Poxa.Console.WSHandler do {method, req} = :cowboy_req.method(req) {path, req} = :cowboy_req.path(req) {qs_vals, req} = :cowboy_req.qs_vals(req) + {app_id, req} = :cowboy_req.binding(:app_id, req) - if Authentication.check(method, path, "", qs_vals) do - :ok = Poxa.Event.add_handler({Poxa.Console, self}, self) + if Authentication.check(app_id, method, path, "", qs_vals) do + :ok = Poxa.Event.add_handler({Poxa.Console, self}, [self, app_id]) {:ok, req, nil} else - Logger.error "Failed to authenticate on Console Websocket" + Logger.error "Failed to authenticate on Console Websocket for app with id: #{app_id}" {:shutdown, req} end end diff --git a/lib/poxa/event.ex b/lib/poxa/event.ex index 87f247c..373e7b0 100644 --- a/lib/poxa/event.ex +++ b/lib/poxa/event.ex @@ -1,5 +1,8 @@ defmodule Poxa.Event do - def notify(event, data), do: :gen_event.sync_notify(Poxa.Event, Map.put(data, :event, event)) + def notify(event, app_id, data) do + poxa_event = data |> Map.put(:event, event) |> Map.put(:app_id, app_id) + :gen_event.sync_notify(Poxa.Event, poxa_event) + end def add_handler(handler, pid) do GenEvent.add_mon_handler(Poxa.Event, handler, pid) diff --git a/lib/poxa/event_handler.ex b/lib/poxa/event_handler.ex index db4f83a..7777f40 100644 --- a/lib/poxa/event_handler.ex +++ b/lib/poxa/event_handler.ex @@ -26,7 +26,8 @@ defmodule Poxa.EventHandler do {:ok, body, req} = :cowboy_req.body(req) case JSX.decode(body) do {:ok, data} -> - case PusherEvent.build(data) do + {app_id, req} = :cowboy_req.binding(:app_id, req) + case PusherEvent.build(app_id, data) do {:ok, event} -> {false, req, %{body: body, event: event}} _ -> req = :cowboy_req.set_resp_body(@invalid_event_json, req) @@ -47,7 +48,8 @@ defmodule Poxa.EventHandler do {qs_vals, req} = :cowboy_req.qs_vals(req) {method, req} = :cowboy_req.method(req) {path, req} = :cowboy_req.path(req) - authorized = Authentication.check(method, path, body, qs_vals) + {app_id, req} = :cowboy_req.binding(:app_id, req) + authorized = Authentication.check(app_id, method, path, body, qs_vals) unless authorized do req = :cowboy_req.set_resp_body(@authentication_error_json, req) end @@ -80,7 +82,7 @@ defmodule Poxa.EventHandler do @doc false def post(req, %{event: event}) do PusherEvent.publish(event) - Event.notify(:api_message, %{channels: event.channels, name: event.name}) + Event.notify(:api_message, event.app_id, %{channels: event.channels, name: event.name}) req = :cowboy_req.set_resp_body("{}", req) {true, req, nil} end diff --git a/lib/poxa/presence_channel.ex b/lib/poxa/presence_channel.ex index aaef41f..a99310d 100644 --- a/lib/poxa/presence_channel.ex +++ b/lib/poxa/presence_channel.ex @@ -8,9 +8,9 @@ defmodule Poxa.PresenceChannel do More info at: http://pusher.com/docs/rest_api#method-get-users """ - @spec users(binary) :: [binary | integer] - def users(channel) do - match = {{:p, :l, {:pusher, channel}}, :_, :'$1'} + @spec users(binary, binary) :: [binary | integer] + def users(channel, app_id) do + match = {{:p, :l, {:pusher, app_id, channel}}, :_, :'$1'} :gproc.select([{match, [], [:'$1']}]) |> Enum.uniq(fn {user_id, _} -> user_id end) |> Enum.map(fn {user_id, _} -> user_id end) @@ -19,9 +19,9 @@ defmodule Poxa.PresenceChannel do @doc """ Returns the number of unique users on a presence channel """ - @spec user_count(binary) :: non_neg_integer - def user_count(channel) do - match = {{:p, :l, {:pusher, channel}}, :_, :'$1'} + @spec user_count(binary, binary) :: non_neg_integer + def user_count(channel, app_id) do + match = {{:p, :l, {:pusher, app_id, channel}}, :_, :'$1'} :gproc.select([{match, [], [:'$1']}]) |> Enum.uniq(fn {user_id, _} -> user_id end) |> Enum.count diff --git a/lib/poxa/presence_subscription.ex b/lib/poxa/presence_subscription.ex index 1d9ef2b..2099708 100644 --- a/lib/poxa/presence_subscription.ex +++ b/lib/poxa/presence_subscription.ex @@ -15,31 +15,31 @@ defmodule Poxa.PresenceSubscription do @type t :: %__MODULE__{channel: binary, channel_data: [{user_id, user_info}]} alias Poxa.PusherEvent - import Poxa.Channel, only: [presence?: 1, subscribed?: 2] + import Poxa.Channel, only: [presence?: 1, subscribed?: 3] require Logger @doc """ Returns a PresenceSubscription struct """ - @spec subscribe!(binary, :jsx.json_term) :: __MODULE__.t - def subscribe!(channel, channel_data) do + @spec subscribe!(binary, binary, :jsx.json_term) :: __MODULE__.t + def subscribe!(app_id, channel, channel_data) do decoded_channel_data = JSX.decode!(channel_data) - if subscribed?(channel, self) do + if subscribed?(app_id, channel, self) do Logger.info "Already subscribed #{inspect self} on channel #{channel}" else Logger.info "Registering #{inspect self} to channel #{channel}" {user_id, user_info} = extract_userid_and_userinfo(decoded_channel_data) - unless user_id_already_on_presence_channel(user_id, channel) do + unless user_id_already_on_presence_channel(app_id, user_id, channel) do message = PusherEvent.presence_member_added(channel, user_id, user_info) - :gproc.send({:p, :l, {:pusher, channel}}, {self, message}) + :gproc.send({:p, :l, {:pusher, app_id, channel}}, {self, message}) end - :gproc.reg({:p, :l, {:pusher, channel}}, {user_id, user_info}) + :gproc.reg({:p, :l, {:pusher, app_id, channel}}, {user_id, user_info}) end - %__MODULE__{channel: channel, channel_data: channel_data(channel)} + %__MODULE__{channel: channel, channel_data: channel_data(app_id, channel)} end - defp channel_data(channel) do - for {_pid, {user_id, user_info}} <- :gproc.lookup_values({:p, :l, {:pusher, channel}}) do + defp channel_data(app_id, channel) do + for {_pid, {user_id, user_info}} <- :gproc.lookup_values({:p, :l, {:pusher, app_id, channel}}) do {user_id, user_info} end |> Enum.uniq(fn {user_id, _} -> user_id end) end @@ -53,8 +53,8 @@ defmodule Poxa.PresenceSubscription do defp sanitize_user_id(user_id) when is_binary(user_id), do: user_id defp sanitize_user_id(user_id), do: JSX.encode!(user_id) - defp user_id_already_on_presence_channel(user_id, channel) do - match = {{:p, :l, {:pusher, channel}}, :_, {user_id, :_}} + defp user_id_already_on_presence_channel(app_id, user_id, channel) do + match = {{:p, :l, {:pusher, app_id, channel}}, :_, {user_id, :_}} :gproc.select_count([{match, [], [true]}]) != 0 end @@ -62,12 +62,12 @@ defmodule Poxa.PresenceSubscription do Unsubscribe from a presence channel, possibly triggering presence_member_removed It respects multiple connections from the same user id """ - @spec unsubscribe!(binary) :: {:ok, binary} - def unsubscribe!(channel) do - case :gproc.get_value({:p, :l, {:pusher, channel}}) do + @spec unsubscribe!(binary, binary) :: {:ok, binary} + def unsubscribe!(app_id, channel) do + case :gproc.get_value({:p, :l, {:pusher, app_id, channel}}) do {user_id, _} -> - if only_one_connection_on_user_id?(channel, user_id) do - presence_member_removed(channel, user_id) + if only_one_connection_on_user_id?(app_id, channel, user_id) do + presence_member_removed(app_id, channel, user_id) end _ -> nil end @@ -80,24 +80,24 @@ defmodule Poxa.PresenceSubscription do If the connection is the only one, fire up the member_removed event """ - @spec check_and_remove :: :ok - def check_and_remove do - match = {{:p, :l, {:pusher, :'$1'}}, self, {:'$2', :_}} + @spec check_and_remove(binary) :: :ok + def check_and_remove(app_id) do + match = {{:p, :l, {:pusher, app_id, :'$1'}}, self, {:'$2', :_}} channel_user_id = :gproc.select([{match, [], [[:'$1',:'$2']]}]) for [channel, user_id] <- channel_user_id, - presence?(channel), only_one_connection_on_user_id?(channel, user_id) do - presence_member_removed(channel, user_id) + presence?(channel), only_one_connection_on_user_id?(app_id, channel, user_id) do + presence_member_removed(app_id, channel, user_id) end :ok end - defp presence_member_removed(channel, user_id) do + defp presence_member_removed(app_id, channel, user_id) do message = PusherEvent.presence_member_removed(channel, user_id) - :gproc.send({:p, :l, {:pusher, channel}}, {self, message}) + :gproc.send({:p, :l, {:pusher, app_id, channel}}, {self, message}) end - defp only_one_connection_on_user_id?(channel, user_id) do - match = {{:p, :l, {:pusher, channel}}, :_, {user_id, :_}} + defp only_one_connection_on_user_id?(app_id, channel, user_id) do + match = {{:p, :l, {:pusher, app_id, channel}}, :_, {user_id, :_}} :gproc.select_count([{match, [], [true]}]) == 1 end end diff --git a/lib/poxa/pusher_event.ex b/lib/poxa/pusher_event.ex index bed95c9..279e4a1 100644 --- a/lib/poxa/pusher_event.ex +++ b/lib/poxa/pusher_event.ex @@ -140,42 +140,42 @@ defmodule Poxa.PusherEvent do data: data} |> encode! end - defstruct [:channels, :name, :data, :socket_id] - @type t :: %Poxa.PusherEvent{channels: list, name: binary, + defstruct [:channels, :name, :app_id, :data, :socket_id] + @type t :: %Poxa.PusherEvent{channels: list, name: binary, app_id: binary, data: binary | map, socket_id: nil | binary} @doc """ Builds the struct based on the decoded JSON from /events endpoint """ - @spec build(map) :: {:ok, Poxa.PusherEvent.t} | {:error, atom} - def build(%{"name" => name, "channels" => channels, "data" => data} = event) do - build_event(channels, data, name, event["socket_id"]) + @spec build(binary, map) :: {:ok, Poxa.PusherEvent.t} | {:error, atom} + def build(app_id, %{"name" => name, "channels" => channels, "data" => data} = event) do + build_event(app_id, channels, data, name, event["socket_id"]) end - def build(%{"name" => name, "channel" => channel, "data" => data} = event) do - build_event([channel], data, name, event["socket_id"]) + def build(app_id, %{"name" => name, "channel" => channel, "data" => data} = event) do + build_event(app_id, [channel], data, name, event["socket_id"]) end - def build(_), do: {:error, :invalid_pusher_event} + def build(_, _), do: {:error, :invalid_pusher_event} @doc """ Build client events """ - @spec build_client_event(map, binary) :: {:ok, Poxa.PusherEvent.t} | {:error, atom} - def build_client_event(%{"event" => name, "channel" => channel, "data" => data}, socket_id) do - build_event([channel], data, name, socket_id) + @spec build_client_event(binary, map, binary) :: {:ok, Poxa.PusherEvent.t} | {:error, atom} + def build_client_event(app_id, %{"event" => name, "channel" => channel, "data" => data}, socket_id) do + build_event(app_id, [channel], data, name, socket_id) end - def build_client_event(%{"name" => name, "channel" => channel, "data" => data}, socket_id) do - build_event([channel], data, name, socket_id) + def build_client_event(app_id, %{"name" => name, "channel" => channel, "data" => data}, socket_id) do + build_event(app_id, [channel], data, name, socket_id) end - defp build_event(channels, data, name, socket_id) do - event = %Poxa.PusherEvent{channels: channels, data: data, name: name, socket_id: socket_id} + defp build_event(app_id, channels, data, name, socket_id) do + event = %Poxa.PusherEvent{app_id: app_id, channels: channels, data: data, name: name, socket_id: socket_id} if valid?(event), do: {:ok, event}, else: {:error, :invalid_event} end - defp valid?(%Poxa.PusherEvent{channels: channels, socket_id: socket_id}) do + defp valid?(%Poxa.PusherEvent{channels: channels, socket_id: socket_id, app_id: app_id}) do Enum.all?(channels, &Poxa.Channel.valid?(&1)) and - (!socket_id || Poxa.SocketId.valid?(socket_id)) + (!socket_id || Poxa.SocketId.valid?(socket_id)) and app_id end defp valid?(_), do: false @@ -192,7 +192,7 @@ defmodule Poxa.PusherEvent do defp publish_event_to_channel(event, channel) do message = build_message(event, channel) |> encode! - :gproc.send({:p, :l, {:pusher, channel}}, {self, message, event.socket_id}) + :gproc.send({:p, :l, {:pusher, event.app_id, channel}}, {self, message, event.socket_id}) end defp build_message(event, channel) do diff --git a/lib/poxa/subscription.ex b/lib/poxa/subscription.ex index e51f8a6..5325a8f 100644 --- a/lib/poxa/subscription.ex +++ b/lib/poxa/subscription.ex @@ -14,40 +14,40 @@ defmodule Poxa.Subscription do Returns {:ok, channel} to public and private channels and a PresenceSubscription to a presence channel """ - @spec subscribe!(:jsx.json_term, binary) :: {:ok, binary} + @spec subscribe!(binary, :jsx.json_term, binary) :: {:ok, binary} | PresenceSubscription.t | {:error, binary} - def subscribe!(data, socket_id) do + def subscribe!(app_id, data, socket_id) do channel = data["channel"] cond do Channel.private?(channel) -> - subscribe_private_channel(socket_id, channel, data["auth"], data["channel_data"]) + subscribe_private_channel(app_id, socket_id, channel, data["auth"], data["channel_data"]) Channel.presence?(channel) -> - subscribe_presence_channel(socket_id, channel, data["auth"], data["channel_data"]) + subscribe_presence_channel(app_id, socket_id, channel, data["auth"], data["channel_data"]) is_binary(channel) -> - subscribe_channel(channel) + subscribe_channel(app_id, channel) true -> Logger.info "Missing channel" {:error, PusherEvent.pusher_error("Missing parameter: data.channel")} end end - defp subscribe_presence_channel(socket_id, channel, auth, channel_data) do + defp subscribe_presence_channel(app_id, socket_id, channel, auth, channel_data) do to_sign = socket_id <> ":" <> channel <> ":" <> (channel_data || "") - if AuthSignature.valid?(to_sign, auth) do - PresenceSubscription.subscribe!(channel, channel_data) + if AuthSignature.valid?(app_id, to_sign, auth) do + PresenceSubscription.subscribe!(app_id, channel, channel_data) else {:error, signature_error(to_sign, auth)} end end - defp subscribe_private_channel(socket_id, channel, auth, channel_data) do + defp subscribe_private_channel(app_id, socket_id, channel, auth, channel_data) do to_sign = case channel_data do nil -> socket_id <> ":" <> channel channel_data -> socket_id <> ":" <> channel <> ":" <> channel_data end - if AuthSignature.valid?(to_sign, auth) do - subscribe_channel(channel) + if AuthSignature.valid?(app_id, to_sign, auth) do + subscribe_channel(app_id, channel) else {:error, signature_error(to_sign, auth)} end @@ -58,13 +58,13 @@ defmodule Poxa.Subscription do PusherEvent.pusher_error(msg) end - defp subscribe_channel(channel) do + defp subscribe_channel(app_id, channel) do Logger.info "Subscribing to channel #{channel}" - if Channel.subscribed?(channel, self) do + if Channel.subscribed?(channel, app_id, self) do Logger.info "Already subscribed #{inspect self} on channel #{channel}" else Logger.info "Registering #{inspect self} to channel #{channel}" - :gproc.reg({:p, :l, {:pusher, channel}}) + :gproc.reg({:p, :l, {:pusher, app_id, channel}}) end {:ok, channel} end @@ -72,14 +72,14 @@ defmodule Poxa.Subscription do @doc """ Unsubscribe from a channel always returning :ok """ - @spec unsubscribe!(:jsx.json_term) :: {:ok, binary} - def unsubscribe!(data) do + @spec unsubscribe!(binary, :jsx.json_term) :: {:ok, binary} + def unsubscribe!(app_id, data) do channel = data["channel"] - if Channel.subscribed?(channel, self) do + if Channel.subscribed?(channel, app_id, self) do if Channel.presence?(channel) do - PresenceSubscription.unsubscribe!(channel); + PresenceSubscription.unsubscribe!(app_id, channel); end - :gproc.unreg({:p, :l, {:pusher, channel}}); + :gproc.unreg({:p, :l, {:pusher, app_id, channel}}); else Logger.debug "Not subscribed to" end diff --git a/lib/poxa/supervisor.ex b/lib/poxa/supervisor.ex index a4c4522..cbc2944 100644 --- a/lib/poxa/supervisor.ex +++ b/lib/poxa/supervisor.ex @@ -10,7 +10,8 @@ defmodule Poxa.Supervisor do The supervisor will spawn a GenEvent named Poxa.Event """ def init([]) do - children = [worker(GenEvent, [[name: Poxa.Event]])] + children = [worker(Poxa.App, []), + worker(GenEvent, [[name: Poxa.Event]])] supervise children, strategy: :one_for_one end end diff --git a/lib/poxa/users_handler.ex b/lib/poxa/users_handler.ex index 12d6cc0..35bc09b 100644 --- a/lib/poxa/users_handler.ex +++ b/lib/poxa/users_handler.ex @@ -49,7 +49,8 @@ defmodule Poxa.UsersHandler do """ def get_json(req, channel) do - response = PresenceChannel.users(channel) |> Enum.map(fn(id) -> [id: id] end) + {app_id, req} = :cowboy_req.binding(:app_id, req) + response = PresenceChannel.users(channel, app_id) |> Enum.map(&([id: &1])) {JSX.encode!(users: response), req, nil} end end diff --git a/lib/poxa/websocket_handler.ex b/lib/poxa/websocket_handler.ex index f81bfde..4077499 100644 --- a/lib/poxa/websocket_handler.ex +++ b/lib/poxa/websocket_handler.ex @@ -8,6 +8,7 @@ defmodule Poxa.WebsocketHandler do More info on Pusher protocol at: http://pusher.com/docs/pusher_protocol """ require Logger + alias Poxa.App alias Poxa.PusherEvent alias Poxa.Event alias Poxa.PresenceSubscription @@ -20,7 +21,7 @@ defmodule Poxa.WebsocketHandler do @min_protocol 5 defmodule State do - defstruct [:socket_id, :time] + defstruct [:socket_id, :time, :app_id] end @doc false @@ -33,18 +34,18 @@ defmodule Poxa.WebsocketHandler do def websocket_init(_transport_name, req, _opts) do {app_key, req} = :cowboy_req.binding(:app_key, req) {protocol, req} = :cowboy_req.qs_val("protocol", req, to_string(@max_protocol)) - case :application.get_env(:poxa, :app_key) do - {:ok, ^app_key} -> + case App.id(app_key) do + {:ok, app_id} -> if supported_protocol?(protocol) do send self, :start - {:ok, req, nil} + {:ok, req, %State{app_id: app_id}} else Logger.error "Protocol #{protocol} not supported" send self, :start_error {:ok, req, {4007, "Unsupported protocol version"}} end - {:ok, expected_app_key} -> - Logger.error "Invalid app_key, expected #{expected_app_key}, found #{app_key}" + {:error, _reason} -> + Logger.error "App not found with key #{app_key}" send self, :start_error {:ok, req, {4001, "Application does not exist"}} end @@ -80,20 +81,20 @@ defmodule Poxa.WebsocketHandler do defp handle_pusher_event("pusher:subscribe", decoded_json, req, %State{socket_id: socket_id} = state) do data = decoded_json["data"] - reply = case Subscription.subscribe!(data, socket_id) do + reply = case Subscription.subscribe!(state.app_id, data, socket_id) do {:ok, channel} -> - Event.notify(:subscribed, %{socket_id: socket_id, channel: channel}) + Event.notify(:subscribed, state.app_id, %{socket_id: socket_id, channel: channel}) PusherEvent.subscription_succeeded(channel) subscription = %PresenceSubscription{channel: channel} -> - Event.notify(:subscribed, %{socket_id: socket_id, channel: channel}) + Event.notify(:subscribed, state.app_id, %{socket_id: socket_id, channel: channel}) PusherEvent.subscription_succeeded(subscription) {:error, error} -> error end {:reply, {:text, reply}, req, state} end defp handle_pusher_event("pusher:unsubscribe", decoded_json, req, %State{socket_id: socket_id} = state) do - {:ok, channel} = Subscription.unsubscribe! decoded_json["data"] - Event.notify(:unsubscribed, %{socket_id: socket_id, channel: channel}) + {:ok, channel} = Subscription.unsubscribe!(state.app_id, decoded_json["data"]) + Event.notify(:unsubscribed, state.app_id, %{socket_id: socket_id, channel: channel}) {:ok, req, state} end defp handle_pusher_event("pusher:ping", _decoded_json, req, state) do @@ -102,11 +103,11 @@ defmodule Poxa.WebsocketHandler do end # Client Events defp handle_pusher_event("client-" <> _event_name, decoded_json, req, %State{socket_id: socket_id} = state) do - {:ok, event} = PusherEvent.build_client_event(decoded_json, socket_id) + {:ok, event} = PusherEvent.build_client_event(state.app_id, decoded_json, socket_id) channel = List.first(event.channels) - if Channel.private_or_presence?(channel) and Channel.subscribed?(channel, self) do + if Channel.private_or_presence?(channel) and Channel.subscribed?(channel, state.app_id, self) do PusherEvent.publish(event) - Event.notify(:client_event_message, %{socket_id: socket_id, channels: event.channels, name: event.name}) + Event.notify(:client_event_message, state.app_id, %{socket_id: socket_id, channels: event.channels, name: event.name}) end {:ok, req, state} end @@ -122,15 +123,15 @@ defmodule Poxa.WebsocketHandler do * start_error - Improper start returning useful error code after unsuccessful websocket_init * msg broadcast - Any message generate by events that gets to the connection """ - def websocket_info(:start, req, _state) do + def websocket_info(:start, req, state) do # Unique identifier for the connection socket_id = SocketId.generate! {origin, req} = :cowboy_req.host_url(req) - Event.notify(:connected, %{socket_id: socket_id, origin: origin}) + Event.notify(:connected, state.app_id, %{socket_id: socket_id, origin: origin}) reply = PusherEvent.connection_established(socket_id) - {:reply, {:text, reply}, req, %State{socket_id: socket_id, time: Time.stamp}} + {:reply, {:text, reply}, req, %{state | socket_id: socket_id, time: Time.stamp}} end def websocket_info(:start_error, req, {code, message}) do @@ -156,11 +157,11 @@ defmodule Poxa.WebsocketHandler do member removal if necessary and explicitly unregister tags on gproc """ def websocket_terminate(_reason, _req, nil), do: :ok - def websocket_terminate(_reason, _req, %State{socket_id: socket_id, time: time}) do - duration = Time.stamp - time - channels = Channel.all(self) - Event.notify(:disconnected, %{socket_id: socket_id, channels: channels, duration: duration}) - PresenceSubscription.check_and_remove + def websocket_terminate(_reason, _req, state) do + duration = Time.stamp - state.time + channels = Channel.all(state.app_id, self) + Event.notify(:disconnected, state.app_id, %{socket_id: state.socket_id, channels: channels, duration: duration}) + PresenceSubscription.check_and_remove(state.app_id) :gproc.goodbye :ok end diff --git a/priv/index.html b/priv/index.html index ed5a166..86c7dfa 100644 --- a/priv/index.html +++ b/priv/index.html @@ -15,6 +15,10 @@
Poxa Console
+
+ + +
diff --git a/priv/static/js/console.js b/priv/static/js/console.js index f4f9536..1d203f7 100644 --- a/priv/static/js/console.js +++ b/priv/static/js/console.js @@ -10,37 +10,40 @@ function init() { }; $('#connect').submit(function(event) { + var appId = $('#appId').val(); var appKey = $('#appKey').val(); var secret = $('#secret').val(); - connect(appKey, secret); + connect(appId, appKey, secret); event.preventDefault(); }); $('#disconnect').submit(function(event) { disconnect(); - var appKey = $('#appKey').val(""); - var secret = $('#secret').val(""); + $('#appId').val(""); + $('#appKey').val(""); + $('#secret').val(""); event.preventDefault(); }); }; -function connect(appKey, secret) { - var qs = authQS(appKey, secret); +function connect(appId, appKey, secret) { + var path = "/apps/" + appId + "/console" + var qs = authQS(appKey, secret, path); var host = window.location.host; - websocket = new WebSocket(websocketProtocol() + "//" + host + "/console?" + qs); + websocket = new WebSocket(websocketProtocol() + "//" + host + path + "?" + qs); websocket.onopen = function(evt) { onOpen(evt) }; websocket.onclose = function(evt) { onClose(evt) }; websocket.onmessage = function(evt) { onMessage(evt) }; websocket.onerror = function(evt) { onError(evt) }; }; -function authQS(appKey, secret) { +function authQS(appKey, secret, path) { var auth_key = appKey; var auth_timestamp = Math.round(new Date().getTime() / 1000); var params = { auth_key:auth_key, auth_timestamp:auth_timestamp, auth_version:"1.0" } ; - var auth_signature = CryptoJS.HmacSHA256("GET\n/console\n" + $.param(params), secret).toString(); + var auth_signature = CryptoJS.HmacSHA256("GET\n" + path +"\n" + $.param(params), secret).toString(); return $.param($.extend({ }, params, { auth_signature:auth_signature })); } diff --git a/test/app_test.exs b/test/app_test.exs new file mode 100644 index 0000000..79ee4d8 --- /dev/null +++ b/test/app_test.exs @@ -0,0 +1,37 @@ +defmodule Poxa.AppTest do + use ExUnit.Case + import Poxa.App + + setup_all do + Application.put_env(:poxa, :apps, [{"app_id2", "app_key2", "app_secret2"}]) + :ok = Poxa.App.reload + :ok + end + + test "search for existing id using the key" do + assert id("app_key") == {:ok, "app_id"} + assert id("app_key2") == {:ok, "app_id2"} + end + + test "search for existing key using the id" do + assert key("app_id") == {:ok, "app_key"} + assert key("app_id2") == {:ok, "app_key2"} + end + + test "search for existing key and secret using the id" do + assert key_secret("app_id") == {:ok, {"app_key", "secret"}} + assert key_secret("app_id2") == {:ok, {"app_key2", "app_secret2"}} + end + + test "search for inexisting id using the key" do + assert id("no_app_key") == {:error, {:key_not_found, "no_app_key"}} + end + + test "search for inexisting key using the id" do + assert key("no_app_id") == {:error, {:id_not_found, "no_app_id"}} + end + + test "search for inexisting key and secret using the id" do + assert key_secret("no_app_id") == {:error, {:id_not_found, "no_app_id"}} + end +end diff --git a/test/auth_signature_test.exs b/test/auth_signature_test.exs index 507495d..5f03850 100644 --- a/test/auth_signature_test.exs +++ b/test/auth_signature_test.exs @@ -1,50 +1,50 @@ defmodule Poxa.AuthSignatureTest do use ExUnit.Case alias Poxa.Authentication + alias Poxa.App import :meck import Poxa.AuthSignature setup do new Authentication + new App on_exit fn -> unload end :ok end test "a valid signature" do - :application.set_env(:poxa, :app_secret, "secret") - app_key = "app_key" + expect(App, :key_secret, [{["app_id"], {:ok, {"app_key", "secret"}}}]) signature = Poxa.CryptoHelper.hmac256_to_string("secret", "SocketId:private-channel") - auth = <> - expect(Authentication, :check_key, 1, true) + auth = "app_key:#{signature}" - assert valid?("SocketId:private-channel", auth) + assert valid?("app_id", "SocketId:private-channel", auth) assert validate Authentication + assert validate App end - test "an invalid key" do - expect(Authentication, :check_key, 1, false) + test "an inexisting app" do + expect(App, :key_secret, [{["app_id"], {:error, :reason}}]) - refute valid?("SocketId:private-channel", "Auth:key") + refute valid?("app_id", "SocketId:private-channel", "Auth:key") assert validate Authentication end - test "an invalid signature" do - :application.set_env(:poxa, :app_secret, "secret") - expect(Authentication, :check_key, 1, true) + test "a different app_key" do + expect(App, :key_secret, [{["app_id"], {:ok, {"app_key", "secret"}}}]) + signature = Poxa.CryptoHelper.hmac256_to_string("secret", "SocketId:private-channel") + auth = "app_key2:#{signature}" - refute valid?("SocketId:private-channel", "Wrong:Auth") + refute valid?("app_id", "SocketId:private-channel", auth) assert validate Authentication end - test "invalid " do - :application.set_env(:poxa, :app_secret, "secret") - expect(Authentication, :check_key, 1, true) + test "invalid auth string" do + expect(App, :key_secret, [{["app_id"], {:ok, {"app_key", "secret"}}}]) - refute valid?("SocketId:private-channel", "asdfasdf") - refute valid?("SocketId:private-channel", "asdfasdf") + refute valid?("app_id", "SocketId:private-channel", "asdfasdf") assert validate Authentication end diff --git a/test/authentication_test.exs b/test/authentication_test.exs index 93170fe..dcbb7a6 100644 --- a/test/authentication_test.exs +++ b/test/authentication_test.exs @@ -2,56 +2,57 @@ defmodule Poxa.AuthenticationTest do use ExUnit.Case import :meck import Poxa.Authentication + alias Poxa.App setup do new Application + new App new Signaturex on_exit fn -> unload end :ok end + test "check with unknown app_key and secret" do + expect(App, :key_secret, [{[:app_id], {:error, :reason}}]) + qs = %{"body_md5" => "841a2d689ad86bd1611447453c22c6fc"} + expect(Signaturex, :validate, [{[:app_key, :secret, 'get', '/url', qs, 600], :ok}]) + + refute check(:app_id, 'get', '/url', 'body', qs) + + assert validate Signaturex + assert validate App + end + test "check with a non-empty body and correct md5" do - expect(Application, :fetch_env, [{[:poxa, :app_key], {:ok, :app_key}}, {[:poxa, :app_secret], {:ok, :secret}}]) + expect(App, :key_secret, [{[:app_id], {:ok, {:app_key, :secret}}}]) qs = %{"body_md5" => "841a2d689ad86bd1611447453c22c6fc"} expect(Signaturex, :validate, [{[:app_key, :secret, 'get', '/url', qs, 600], :ok}]) - assert check('get', '/url', 'body', qs) + assert check(:app_id, 'get', '/url', 'body', qs) assert validate Signaturex - assert validate Application + assert validate App end test "check with a non-empty body and incorrect md5" do - expect(Application, :fetch_env, [{[:poxa, :app_key], {:ok, :app_key}}, {[:poxa, :app_secret], {:ok, :secret}}]) + expect(App, :key_secret, [{[:app_id], {:ok, {:app_key, :secret}}}]) qs = %{"body_md5" => "md5?"} expect(Signaturex, :validate, [{[:app_key, :secret, 'get', '/url', qs, 600], :ok}]) - refute check('get', '/url', 'body', qs) + refute check(:app_id, 'get', '/url', 'body', qs) assert validate Signaturex - assert validate Application + assert validate App end test "check with an empty body" do - expect(Application, :fetch_env, [{[:poxa, :app_key], {:ok, :app_key}}, {[:poxa, :app_secret], {:ok, :secret}}]) + expect(App, :key_secret, [{[:app_id], {:ok, {:app_key, :secret}}}]) qs = %{} expect(Signaturex, :validate, [{[:app_key, :secret, "get", "/url", qs, 600], :ok}]) - assert check("get", "/url", "", qs) + assert check(:app_id, "get", "/url", "", qs) assert validate Signaturex - assert validate Application - end - - test "a valid auth_key" do - expect(Application, :fetch_env, 2, {:ok, :auth_key}) - assert check_key(:auth_key) - assert validate Application - end - - test "an invalid auth_key" do - expect(Application, :fetch_env, 2, {:ok, :auth_key}) - refute check_key(:invalid_auth_key) - assert validate Application + assert validate App end end diff --git a/test/authorization_helper_test.exs b/test/authorization_helper_test.exs index ebff0ef..c6cfa7f 100644 --- a/test/authorization_helper_test.exs +++ b/test/authorization_helper_test.exs @@ -16,8 +16,9 @@ defmodule Poxa.AuthorizarionHelperTest do expect(:cowboy_req, :method, 1, {:method, :req2}) expect(:cowboy_req, :qs_vals, 1, {:qs_vals, :req3}) expect(:cowboy_req, :path, 1, {:path, :req4}) - expect(Authentication, :check, 4, true) - assert is_authorized(:req, :state) == {true, :req4, :state} + expect(:cowboy_req, :binding, [{[:app_id, :req4], {:app_id, :req5}}]) + expect(Authentication, :check, [{[:app_id, :method, :path, :body, :qs_vals], true}]) + assert is_authorized(:req, :state) == {true, :req5, :state} assert validate(Authentication) assert validate(:cowboy_req) end @@ -27,8 +28,9 @@ defmodule Poxa.AuthorizarionHelperTest do expect(:cowboy_req, :method, 1, {:method, :req2}) expect(:cowboy_req, :qs_vals, 1, {:qs_vals, :req3}) expect(:cowboy_req, :path, 1, {:path, :req4}) - expect(Authentication, :check, 4, false) - assert is_authorized(:req, :state) == {{false, "authentication failed"}, :req4, nil} + expect(:cowboy_req, :binding, [{[:app_id, :req4], {:app_id, :req5}}]) + expect(Authentication, :check, [{[:app_id, :method, :path, :body, :qs_vals], false}]) + assert is_authorized(:req, :state) == {{false, "authentication failed"}, :req5, nil} assert validate(Authentication) assert validate(:cowboy_req) end diff --git a/test/channel_test.exs b/test/channel_test.exs index 850461c..5c4d29a 100644 --- a/test/channel_test.exs +++ b/test/channel_test.exs @@ -5,48 +5,55 @@ defmodule Poxa.ChannelTest do doctest Poxa.Channel test "occupied? returns false for empty channel" do - refute occupied?("a channel") + register_to_channel("other_app_id", "a channel") + + refute occupied?("a channel", "app_id") end test "occupied? returns true for populated channel" do - register_to_channel("a channel") + register_to_channel("app_id", "a channel") - assert occupied?("a channel") + assert occupied?("a channel", "app_id") end test "list all channels" do - register_to_channel("channel1") - register_to_channel("channel2") - spawn_registered_process("channel3") + register_to_channel("other_app_id", "other_channel") + register_to_channel("app_id", "channel1") + register_to_channel("app_id", "channel2") + spawn_registered_process("app_id", "channel3") - assert "channel1" in all - assert "channel2" in all - assert "channel3" in all + assert "channel1" in all("app_id") + assert "channel2" in all("app_id") + assert "channel3" in all("app_id") + refute "other_channel" in all("app_id") end test "list channels of a pid" do - register_to_channel("channel1") - register_to_channel("channel2") - register_to_channel("channel3") + register_to_channel("app_id", "channel1") + register_to_channel("app_id", "channel2") + register_to_channel("app_id", "channel3") - assert all(self) == ["channel1", "channel2", "channel3"] + assert all("app_id", self) == ["channel1", "channel2", "channel3"] end test "channel is subscribed? returning true" do - register_to_channel("channel") + register_to_channel("app_id", "channel") - assert subscribed?("channel", self) + assert subscribed?("channel", "app_id", self) end test "channel is subscribed returning false" do - refute subscribed?("channel", self) + register_to_channel("other_app_id", "channel") + register_to_channel("app_id", "other_channel") + + refute subscribed?("channel", "app_id", self) end test "subscription_count on channel" do - register_to_channel("channel") - spawn_registered_process("channel") + register_to_channel("app_id", "channel") + spawn_registered_process("app_id", "channel") - assert subscription_count("channel") == 2 + assert subscription_count("channel", "app_id") == 2 end test "valid? return true for valid characters" do diff --git a/test/channels_handler_test.exs b/test/channels_handler_test.exs index c3f0434..c9237d3 100644 --- a/test/channels_handler_test.exs +++ b/test/channels_handler_test.exs @@ -15,8 +15,9 @@ defmodule Poxa.ChannelsHandlerTest do test "malformed_request on multiple channels with no attributes" do expect(:cowboy_req, :qs_val, 3, seq([{"", :req}, {nil, :req}])) expect(:cowboy_req, :binding, 3, {nil, :req}) + expect(:cowboy_req, :binding, 2, {"app_id", :req}) - assert malformed_request(:req, :state) == {false, :req, {:all, nil, []}} + assert malformed_request(:req, :state) == {false, :req, {:all, "app_id", nil, []}} assert validate :cowboy_req end @@ -24,8 +25,9 @@ defmodule Poxa.ChannelsHandlerTest do test "malformed_request on multiple channels with valid attributes" do expect(:cowboy_req, :qs_val, 3, seq([{"subscription_count", :req}, {nil, :req}])) expect(:cowboy_req, :binding, 3, {nil, :req}) + expect(:cowboy_req, :binding, 2, {"app_id", :req}) - assert malformed_request(:req, :state) == {false, :req, {:all, nil, ["subscription_count"]}} + assert malformed_request(:req, :state) == {false, :req, {:all, "app_id", nil, ["subscription_count"]}} assert validate :cowboy_req end @@ -33,8 +35,9 @@ defmodule Poxa.ChannelsHandlerTest do test "malformed_request on multiple channels with invalid attributes" do expect(:cowboy_req, :qs_val, 3, seq([{"user_count", :req}, {nil, :req}])) expect(:cowboy_req, :binding, 3, {nil, :req}) + expect(:cowboy_req, :binding, 2, {"app_id", :req}) - assert malformed_request(:req, :state) == {true, :req, {:all, nil, ["user_count"]}} + assert malformed_request(:req, :state) == {true, :req, {:all, "app_id", nil, ["user_count"]}} assert validate :cowboy_req end @@ -42,8 +45,9 @@ defmodule Poxa.ChannelsHandlerTest do test "malformed_request on multiple channels with filter" do expect(:cowboy_req, :qs_val, 3, seq([{"", :req}, {"poxa-", :req}])) expect(:cowboy_req, :binding, 3, {nil, :req}) + expect(:cowboy_req, :binding, 2, {"app_id", :req}) - assert malformed_request(:req, :state) == {false, :req, {:all, "poxa-", []}} + assert malformed_request(:req, :state) == {false, :req, {:all, "app_id", "poxa-", []}} assert validate :cowboy_req end @@ -51,8 +55,9 @@ defmodule Poxa.ChannelsHandlerTest do test "malformed_request on multiple channels with presence filter and user_count attribute" do expect(:cowboy_req, :qs_val, 3, seq([{"user_count", :req}, {"presence-", :req}])) expect(:cowboy_req, :binding, 3, {nil, :req}) + expect(:cowboy_req, :binding, 2, {"app_id", :req}) - assert malformed_request(:req, :state) == {false, :req, {:all, "presence-", ["user_count"]}} + assert malformed_request(:req, :state) == {false, :req, {:all, "app_id", "presence-", ["user_count"]}} assert validate :cowboy_req end @@ -60,8 +65,9 @@ defmodule Poxa.ChannelsHandlerTest do test "malformed_request on single channel with invalid attributes" do expect(:cowboy_req, :qs_val, 3, {"subscription_count,message_count", :req}) expect(:cowboy_req, :binding, 3, {"channel", :req}) + expect(:cowboy_req, :binding, 2, {"app_id", :req}) - assert malformed_request(:req, :state) == {true, :req, {:one, "channel", ["subscription_count", "message_count"]}} + assert malformed_request(:req, :state) == {true, :req, {:one, "app_id", "channel", ["subscription_count", "message_count"]}} assert validate :cowboy_req end @@ -69,8 +75,9 @@ defmodule Poxa.ChannelsHandlerTest do test "malformed_request on single channel with invalid attribute" do expect(:cowboy_req, :qs_val, 3, {"message_count", :req}) expect(:cowboy_req, :binding, 3, {"channel", :req}) + expect(:cowboy_req, :binding, 2, {"app_id", :req}) - assert malformed_request(:req, :state) == {true, :req, {:one, "channel", ["message_count"]}} + assert malformed_request(:req, :state) == {true, :req, {:one, "app_id", "channel", ["message_count"]}} assert validate :cowboy_req end @@ -78,8 +85,9 @@ defmodule Poxa.ChannelsHandlerTest do test "malformed_request on single channel with valid attributes" do expect(:cowboy_req, :qs_val, 3, {"subscription_count", :req}) expect(:cowboy_req, :binding, 3, {"channel", :req}) + expect(:cowboy_req, :binding, 2, {"app_id", :req}) - assert malformed_request(:req, :state) == {false, :req, {:one, "channel", ["subscription_count"]}} + assert malformed_request(:req, :state) == {false, :req, {:one, "app_id", "channel", ["subscription_count"]}} assert validate :cowboy_req end @@ -87,8 +95,9 @@ defmodule Poxa.ChannelsHandlerTest do test "malformed_request on single non presence-channel with invalid attributes" do expect(:cowboy_req, :qs_val, 3, {"user_count", :req}) expect(:cowboy_req, :binding, 3, {"channel", :req}) + expect(:cowboy_req, :binding, 2, {"app_id", :req}) - assert malformed_request(:req, :state) == {true, :req, {:one, "channel", ["user_count"]}} + assert malformed_request(:req, :state) == {true, :req, {:one, "app_id", "channel", ["user_count"]}} assert validate :cowboy_req end @@ -96,31 +105,32 @@ defmodule Poxa.ChannelsHandlerTest do test "malformed_request on single presence-channel with invalid attributes" do expect(:cowboy_req, :qs_val, 3, {"user_count,subscription_count", :req}) expect(:cowboy_req, :binding, 3, {"presence-channel", :req}) + expect(:cowboy_req, :binding, 2, {"app_id", :req}) - assert malformed_request(:req, :state) == {false, :req, {:one, "presence-channel", ["user_count", "subscription_count"]}} + assert malformed_request(:req, :state) == {false, :req, {:one, "app_id", "presence-channel", ["user_count", "subscription_count"]}} assert validate :cowboy_req end test "get_json on a specific channel with subscription_count attribute" do - expect(Channel, :occupied?, 1, true) - expect(Channel, :subscription_count, 1, 5) + expect(Channel, :occupied?, 2, true) + expect(Channel, :subscription_count, 2, 5) expected = [occupied: true, subscription_count: 5] expect(JSX, :encode!, [{[expected], :encoded_json}]) - assert get_json(:req, {:one, :channel, ["subscription_count"]}) == {:encoded_json, :req, nil} + assert get_json(:req, {:one, "app_id", :channel, ["subscription_count"]}) == {:encoded_json, :req, nil} assert validate JSX assert validate Channel end test "get_json on a specific channel with user_count attribute" do - expect(Channel, :occupied?, 1, true) - expect(PresenceChannel, :user_count, 1, 3) + expect(Channel, :occupied?, 2, true) + expect(PresenceChannel, :user_count, 2, 3) expected = [occupied: true, user_count: 3] expect(JSX, :encode!, [{[expected], :encoded_json}]) - assert get_json(:req, {:one, :channel, ["user_count"]}) == {:encoded_json, :req, nil} + assert get_json(:req, {:one, "app_id", :channel, ["user_count"]}) == {:encoded_json, :req, nil} assert validate JSX assert validate Channel @@ -128,13 +138,13 @@ defmodule Poxa.ChannelsHandlerTest do end test "get_json on a specific channel with user_count and subscription_count attributes" do - expect(Channel, :occupied?, 1, true) - expect(Channel, :subscription_count, 1, 5) - expect(PresenceChannel, :user_count, 1, 3) + expect(Channel, :occupied?, 2, true) + expect(Channel, :subscription_count, 2, 5) + expect(PresenceChannel, :user_count, 2, 3) expected = [occupied: true, subscription_count: 5, user_count: 3] expect(JSX, :encode!, [{[expected], :encoded_json}]) - assert get_json(:req, {:one, :channel, ["subscription_count", "user_count"]}) == {:encoded_json, :req, nil} + assert get_json(:req, {:one, "app_id", :channel, ["subscription_count", "user_count"]}) == {:encoded_json, :req, nil} assert validate JSX assert validate Channel @@ -143,13 +153,13 @@ defmodule Poxa.ChannelsHandlerTest do test "get_json on every single channel" do expect(:cowboy_req, :qs_val, 3, {nil, :req}) - expect(Channel, :all, 0, ["presence-channel", "private-channel"]) + expect(Channel, :all, 1, ["presence-channel", "private-channel"]) expect(Channel, :presence?, 1, true) - expect(PresenceChannel, :user_count, 1, 3) + expect(PresenceChannel, :user_count, 2, 3) expected = [channels: [{"presence-channel", user_count: 3}]] expect(JSX, :encode!, [{[expected], :encoded_json}]) - assert get_json(:req, {:all, nil, ["user_count"]}) == {:encoded_json, :req, nil} + assert get_json(:req, {:all, "app_id", nil, ["user_count"]}) == {:encoded_json, :req, nil} assert validate JSX assert validate Channel @@ -157,12 +167,12 @@ defmodule Poxa.ChannelsHandlerTest do end test "get_json on every single channel with filter" do - expect(Channel, :all, 0, ["presence-channel", "poxa-channel"]) + expect(Channel, :all, 1, ["presence-channel", "poxa-channel"]) expect(Channel, :matches?, 2, seq([false, true])) expected = [channels: [{"poxa-channel", []}]] expect(JSX, :encode!, [{[expected], :encoded_json}]) - assert get_json(:req, {:all, 'poxa-', []}) == {:encoded_json, :req, nil} + assert get_json(:req, {:all, "app_id", "poxa-", []}) == {:encoded_json, :req, nil} assert validate JSX assert validate Channel diff --git a/test/console/console_test.exs b/test/console/console_test.exs index 4d5780e..362c762 100644 --- a/test/console/console_test.exs +++ b/test/console/console_test.exs @@ -4,44 +4,43 @@ defmodule Poxa.ConsoleTest do import Poxa.Console setup_all do - new JSX expect(JSX, :encode!, &(&1)) on_exit fn -> unload end :ok end test "connected" do - handle_event(%{event: :connected, socket_id: "socket_id", origin: "origin"}, self) + handle_event(%{event: :connected, app_id: "app_id", socket_id: "socket_id", origin: "origin"}, {self, "app_id"}) assert_received %{details: "Origin: origin", socket: "socket_id", time: _, type: "Connection"} end test "disconnected" do - handle_event(%{event: :disconnected, socket_id: "socket_id", channels: ["channel1"], duration: 123}, self) + handle_event(%{event: :disconnected, app_id: "app_id", socket_id: "socket_id", channels: ["channel1"], duration: 123}, {self, "app_id"}) assert_received %{details: "Channels: [\"channel1\"], Lifetime: 123s", socket: "socket_id", time: _, type: "Disconnection"} end test "subscribed" do - handle_event(%{event: :subscribed, socket_id: "socket_id", channel: "channel"}, self) + handle_event(%{event: :subscribed, app_id: "app_id", socket_id: "socket_id", channel: "channel"}, {self, "app_id"}) assert_received %{details: "Channel: channel", socket: "socket_id", time: _, type: "Subscribed"} end test "unsubscribed" do - handle_event(%{event: :unsubscribed, socket_id: "socket_id", channel: "channel"}, self) + handle_event(%{event: :unsubscribed, app_id: "app_id", socket_id: "socket_id", channel: "channel"}, {self, "app_id"}) assert_received %{details: "Channel: channel", socket: "socket_id", time: _, type: "Unsubscribed"} end test "api_message" do - handle_event(%{event: :api_message, channels: ["channel"], name: "event_message"}, self) + handle_event(%{event: :api_message, app_id: "app_id", channels: ["channel"], name: "event_message"}, {self, "app_id"}) assert_received %{details: "Channels: [\"channel\"], Event: event_message", socket: "", time: _, type: "API Message"} end test "client_event_message" do - handle_event(%{event: :client_event_message, socket_id: "socket_id", channels: ["channel"], name: "event_message"}, self) + handle_event(%{event: :client_event_message, app_id: "app_id", socket_id: "socket_id", channels: ["channel"], name: "event_message"}, {self, "app_id"}) assert_received %{details: "Channels: [\"channel\"], Event: event_message", socket: "socket_id", time: _, type: "Client Event Message"} end diff --git a/test/console/console_ws_handler_test.exs b/test/console/console_ws_handler_test.exs index 0cd8909..beeca96 100644 --- a/test/console/console_ws_handler_test.exs +++ b/test/console/console_ws_handler_test.exs @@ -17,10 +17,11 @@ defmodule Poxa.Console.WSHandlerTest do expect(:cowboy_req, :method, 1, {:method, :req2}) expect(:cowboy_req, :path, 1, {:path, :req3}) expect(:cowboy_req, :qs_vals, 1, {:qs_vals, :req4}) - expect(Authentication, :check, [{[:method, :path, "", :qs_vals], true}]) - expect(Event, :add_handler, [{[{Poxa.Console, self}, self], :ok}]) + expect(:cowboy_req, :binding, 2, {:app_id, :req5}) + expect(Authentication, :check, [{[:app_id, :method, :path, "", :qs_vals], true}]) + expect(Event, :add_handler, [{[{Poxa.Console, self}, [self, :app_id]], :ok}]) - assert websocket_init(:tranport, :req, []) == {:ok, :req4, nil} + assert websocket_init(:tranport, :req, []) == {:ok, :req5, nil} assert validate Authentication assert validate :cowboy_req @@ -30,9 +31,10 @@ defmodule Poxa.Console.WSHandlerTest do expect(:cowboy_req, :method, 1, {:method, :req2}) expect(:cowboy_req, :path, 1, {:path, :req3}) expect(:cowboy_req, :qs_vals, 1, {:qs_vals, :req4}) - expect(Authentication, :check, [{[:method, :path, "", :qs_vals], false}]) + expect(:cowboy_req, :binding, 2, {:app_id, :req5}) + expect(Authentication, :check, [{[:app_id, :method, :path, "", :qs_vals], false}]) - assert websocket_init(:tranport, :req, []) == {:shutdown, :req4} + assert websocket_init(:tranport, :req, []) == {:shutdown, :req5} assert validate Authentication assert validate :cowboy_req diff --git a/test/event_handler_test.exs b/test/event_handler_test.exs index 68717e5..ad1f142 100644 --- a/test/event_handler_test.exs +++ b/test/event_handler_test.exs @@ -14,10 +14,11 @@ defmodule Poxa.EventHandlerTest do test "malformed_request with valid data" do event = %PusherEvent{} expect(:cowboy_req, :body, 1, {:ok, :body, :req1}) + expect(:cowboy_req, :binding, 2, {"app_id", :req2}) expect(JSX, :decode, 1, {:ok, :data}) - expect(PusherEvent, :build, [{[:data], {:ok, event}}]) + expect(PusherEvent, :build, [{["app_id", :data], {:ok, event}}]) - assert malformed_request(:req, :state) == {false, :req1, %{body: :body, event: event}} + assert malformed_request(:req, :state) == {false, :req2, %{body: :body, event: event}} assert validate :cowboy_req assert validate JSX @@ -36,36 +37,39 @@ defmodule Poxa.EventHandlerTest do test "malformed_request with invalid data" do expect(:cowboy_req, :body, 1, {:ok, :body, :req1}) - expect(:cowboy_req, :set_resp_body, 2, :req2) + expect(:cowboy_req, :binding, 2, {"app_id", :req2}) + expect(:cowboy_req, :set_resp_body, 2, :req3) expect(JSX, :decode, 1, {:ok, :data}) - expect(PusherEvent, :build, [{[:data], {:error, :reason}}]) + expect(PusherEvent, :build, [{["app_id", :data], {:error, :reason}}]) - assert malformed_request(:req, :state) == {true, :req2, :state} + assert malformed_request(:req, :state) == {true, :req3, :state} assert validate :cowboy_req assert validate JSX end test "is_authorized with failing authentication" do - expect(Authentication, :check, 4, false) - expect(:cowboy_req, :qs_vals, 1, {:qsvals, :req2}) - expect(:cowboy_req, :method, 1, {:method, :req3}) + expect(Authentication, :check, 5, false) + expect(:cowboy_req, :qs_vals, 1, {:qsvals, :req1}) + expect(:cowboy_req, :method, 1, {:method, :req2}) expect(:cowboy_req, :path, 1, {:path, :req3}) - expect(:cowboy_req, :set_resp_body, 2, :req4) + expect(:cowboy_req, :binding, [{[:app_id, :req3], {:app_id, :req4}}]) + expect(:cowboy_req, :set_resp_body, 2, :req5) - assert is_authorized(:req, %{body: :body}) == {false, :req4, %{body: :body}} + assert is_authorized(:req, %{body: :body}) == {false, :req5, %{body: :body}} assert validate Authentication assert validate :cowboy_req end test "is_authorized with correct authentication" do - expect(Authentication, :check, 4, true) + expect(Authentication, :check, 5, true) expect(:cowboy_req, :qs_vals, 1, {:qsvals, :req2}) expect(:cowboy_req, :method, 1, {:method, :req3}) expect(:cowboy_req, :path, 1, {:path, :req3}) + expect(:cowboy_req, :binding, [{[:app_id, :req3], {:app_id, :req4}}]) - assert is_authorized(:req, %{body: :body}) == {true, :req3, %{body: :body}} + assert is_authorized(:req, %{body: :body}) == {true, :req4, %{body: :body}} assert validate Authentication assert validate :cowboy_req @@ -89,7 +93,7 @@ defmodule Poxa.EventHandlerTest do test "post event" do event = %PusherEvent{} expect(PusherEvent, :publish, [{[event], :ok}]) - expect(Event, :notify, 2, :ok) + expect(Event, :notify, 3, :ok) expect(:cowboy_req, :set_resp_body, 2, :req2) assert post(:req, %{event: event}) == {true, :req2, nil} diff --git a/test/presence_channel_test.exs b/test/presence_channel_test.exs index 2f21d33..2a0ceb0 100644 --- a/test/presence_channel_test.exs +++ b/test/presence_channel_test.exs @@ -4,17 +4,19 @@ defmodule Poxa.PresenceChannelTest do import Poxa.PresenceChannel test "return unique user ids currently subscribed" do - register_to_channel("presence-channel-test", {:user_id, :user_info}) - spawn_registered_process("presence-channel-test", {:user_id, :user_info}) - spawn_registered_process("presence-channel-test", {:user_id2, :user_info2}) + register_to_channel("other_app_id", "presence-channel-test", {:other_user_id, :other_user_info}) + register_to_channel("app_id", "presence-channel-test", {:user_id, :user_info}) + spawn_registered_process("app_id", "presence-channel-test", {:user_id, :user_info}) + spawn_registered_process("app_id", "presence-channel-test", {:user_id2, :user_info2}) - assert users("presence-channel-test") == [:user_id, :user_id2] + assert users("presence-channel-test", "app_id") == [:user_id, :user_id2] end test "return number of unique subscribed users" do - register_to_channel("presence-channel-test2", {:user_id, :user_info}) - spawn_registered_process("presence-channel-test2", {:user_id2, :user_info2}) + register_to_channel("other_app_id", "presence-channel-test2", {:other_user_id, :other_user_info}) + register_to_channel("app_id", "presence-channel-test2", {:user_id, :user_info}) + spawn_registered_process("app_id", "presence-channel-test2", {:user_id2, :user_info2}) - assert user_count("presence-channel-test2") == 2 + assert user_count("presence-channel-test2", "app_id") == 2 end end diff --git a/test/presence_subscription_test.exs b/test/presence_subscription_test.exs index a9ad37d..7c2d205 100644 --- a/test/presence_subscription_test.exs +++ b/test/presence_subscription_test.exs @@ -14,7 +14,7 @@ defmodule Poxa.PresenceSubscriptionTest do end test "subscribe to a presence channel" do - expect(Channel, :subscribed?, 2, false) + expect(Channel, :subscribed?, 3, false) expect(:gproc, :select_count, 1, 0) expect(:gproc, :send, 2, :sent) expect(:gproc, :reg, 2, :registered) @@ -23,7 +23,7 @@ defmodule Poxa.PresenceSubscriptionTest do other_user = {"id", "info"} expect(:gproc, :lookup_values, 1, [{:pid, user}, {:other_pid, other_user}]) - assert subscribe!("presence-channel", "{ \"user_id\" : \"id123\", \"user_info\" : \"info456\" }") == %PresenceSubscription{channel: "presence-channel", channel_data: [user, other_user]} + assert subscribe!("app_id", "presence-channel", "{ \"user_id\" : \"id123\", \"user_info\" : \"info456\" }") == %PresenceSubscription{channel: "presence-channel", channel_data: [user, other_user]} assert validate Channel assert validate PusherEvent @@ -31,24 +31,24 @@ defmodule Poxa.PresenceSubscriptionTest do end test "subscribe to a presence channel already subscribed" do - expect(Channel, :subscribed?, 2, true) + expect(Channel, :subscribed?, 3, true) user = {"id123", "info456"} expect(:gproc, :lookup_values, 1, [{:pid, user}]) - assert subscribe!("presence-channel", "{ \"user_id\" : \"id123\", \"user_info\" : \"info456\" }") == %PresenceSubscription{channel: "presence-channel", channel_data: [user]} + assert subscribe!("app_id", "presence-channel", "{ \"user_id\" : \"id123\", \"user_info\" : \"info456\" }") == %PresenceSubscription{channel: "presence-channel", channel_data: [user]} assert validate Channel assert validate :gproc end test "subscribe to a presence channel having the same userid" do - expect(Channel, :subscribed?, 2, false) + expect(Channel, :subscribed?, 3, false) expect(:gproc, :select_count, 1, 1) # true for user_id_already_on_presence_channel/2 expect(:gproc, :reg, 2, :registered) user = {"id123", "info456"} expect(:gproc, :lookup_values, 1, [{:pid, user}, {:other_pid, user}]) - assert subscribe!("presence-channel", "{ \"user_id\" : \"id123\", \"user_info\" : \"info456\" }") == %PresenceSubscription{channel: "presence-channel", channel_data: [user]} + assert subscribe!("app_id", "presence-channel", "{ \"user_id\" : \"id123\", \"user_info\" : \"info456\" }") == %PresenceSubscription{channel: "presence-channel", channel_data: [user]} assert validate Channel assert validate PusherEvent @@ -60,7 +60,7 @@ defmodule Poxa.PresenceSubscriptionTest do expect(:gproc, :get_value, 1, {:userid, :userinfo}) expect(:gproc, :send, 2, :sent) expect(PusherEvent, :presence_member_removed, 2, :event_message) - assert unsubscribe!("presence-channel") == {:ok, "presence-channel"} + assert unsubscribe!("app_id", "presence-channel") == {:ok, "presence-channel"} assert validate PusherEvent assert validate :gproc end @@ -68,7 +68,7 @@ defmodule Poxa.PresenceSubscriptionTest do test "unsubscribe to presence channel being not subscribed" do expect(:gproc, :select_count, 1, 0) expect(:gproc, :get_value, 1, nil) - assert unsubscribe!("presence-channel") == {:ok, "presence-channel"} + assert unsubscribe!("app_id", "presence-channel") == {:ok, "presence-channel"} assert validate :gproc end @@ -77,7 +77,7 @@ defmodule Poxa.PresenceSubscriptionTest do expect(:gproc, :get_value, 1, {:userid, :userinfo}) expect(:gproc, :send, 2, :sent) expect(PusherEvent, :presence_member_removed, 2, :event_message) - assert unsubscribe!("presence-channel") == {:ok, "presence-channel"} + assert unsubscribe!("app_id", "presence-channel") == {:ok, "presence-channel"} assert validate PusherEvent assert validate :gproc end @@ -87,7 +87,9 @@ defmodule Poxa.PresenceSubscriptionTest do expect(:gproc, :select_count, 1, 1) expect(PusherEvent, :presence_member_removed, 2, :msg) expect(:gproc, :send, 2, :ok) - assert check_and_remove == :ok + + assert check_and_remove("app_id") == :ok + assert validate PusherEvent assert validate :gproc end @@ -95,14 +97,14 @@ defmodule Poxa.PresenceSubscriptionTest do test "check subscribed presence channels and remove having more than one connection on userid" do expect(:gproc, :select, 1, [["presence-channel", :userid]]) expect(:gproc, :select_count, 1, 5) - assert check_and_remove == :ok + assert check_and_remove("app_id") == :ok assert validate PusherEvent assert validate :gproc end test "check subscribed presence channels and find nothing to unsubscribe" do expect(:gproc, :select, 1, []) - assert check_and_remove == :ok + assert check_and_remove("app_id") == :ok assert validate :gproc end end diff --git a/test/pusher_event_test.exs b/test/pusher_event_test.exs index 3863c94..3a5a6a7 100644 --- a/test/pusher_event_test.exs +++ b/test/pusher_event_test.exs @@ -9,8 +9,6 @@ defmodule Poxa.PusherEventTest do :ok end - doctest Poxa.PusherEvent - test "connection established output" do json ="{\"data\":\"{\\\"activity_timeout\\\":120,\\\"socket_id\\\":\\\"SocketId\\\"}\",\"event\":\"pusher:connection_established\"}" assert connection_established("SocketId") == json @@ -62,14 +60,15 @@ defmodule Poxa.PusherEventTest do event = %{"channel" => "channel_name", "data" => "event_data", "name" => "event_etc"} - assert build(event) == {:ok, %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name"]}} + assert build("app_id", event) == {:ok, %PusherEvent{app_id: "app_id", name: "event_etc", data: "event_data", channels: ["channel_name"]}} end test "build PusherEvent with multiple channels" do event = %{"channels" => ["channel_name1", "channel_name2"], "data" => "event_data", "name" => "event_etc"} - assert build(event) == {:ok, %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name1","channel_name2"]}} + assert build("app_id", event) == + {:ok, %PusherEvent{app_id: "app_id", name: "event_etc", data: "event_data", channels: ["channel_name1","channel_name2"]}} end test "build PusherEvent with excluding socket_id" do @@ -77,7 +76,14 @@ defmodule Poxa.PusherEventTest do "data" => "event_data", "socket_id" => "123.456", "name" => "event_etc"} - assert build(event) == {:ok, %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name"], socket_id: "123.456"}} + assert build("app_id", event) == {:ok, %PusherEvent{app_id: "app_id", name: "event_etc", data: "event_data", channels: ["channel_name"], socket_id: "123.456"}} + end + + test "build PusherEvent with no app_id" do + event = %{"channel" => "channel_name", + "data" => "event_data", + "name" => "event_etc"} + assert build(nil, event) == {:error, :invalid_event} end test "build PusherEvent with invalid socket_id" do @@ -85,7 +91,7 @@ defmodule Poxa.PusherEventTest do "data" => "event_data", "socket_id" => "123:456", "name" => "event_etc"} - assert build(event) == {:error, :invalid_event} + assert build("app_id", event) == {:error, :invalid_event} end test "build PusherEvent with invalid channel name" do @@ -93,35 +99,36 @@ defmodule Poxa.PusherEventTest do "data" => "event_data", "socket_id" => "123.456", "name" => "event_etc"} - assert build(event) == {:error, :invalid_event} + assert build("app_id", event) == {:error, :invalid_event} end test "build_client_event with excluding socket_id" do event = %{"channel" => "channel_name", "data" => "event_data", "name" => "event_etc"} - assert build_client_event(event, "123.456") == {:ok, %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name"], socket_id: "123.456"}} + assert build_client_event("app_id", event, "123.456") == + {:ok, %PusherEvent{app_id: "app_id", name: "event_etc", data: "event_data", channels: ["channel_name"], socket_id: "123.456"}} end test "build_client_event with invalid excluding socket_id" do event = %{"channel" => "channel_name", "data" => "event_data", "name" => "event_etc"} - assert build_client_event(event, "123:456") == {:error, :invalid_event} + assert build_client_event("app_id", event, "123:456") == {:error, :invalid_event} end test "build_client_event with invalid channel name" do event = %{"channel" => "channel:name", "data" => "event_data", "name" => "event_etc"} - assert build_client_event(event, "123.456") == {:error, :invalid_event} + assert build_client_event("app_id", event, "123.456") == {:error, :invalid_event} end test "sending message to a channel" do - expect(:gproc, :send, [{[{:p, :l, {:pusher, "channel123"}}, {self, :msg, nil}], :ok}]) + expect(:gproc, :send, [{[{:p, :l, {:pusher, "app_id", "channel123"}}, {self, :msg, nil}], :ok}]) expected = %{channel: "channel123", data: "data", event: "event"} expect(JSX, :encode!, [{[expected], :msg}]) - event = %PusherEvent{channels: ["channel123"], data: "data", name: "event"} + event = %PusherEvent{app_id: "app_id", channels: ["channel123"], data: "data", name: "event"} assert publish(event) == :ok @@ -131,9 +138,9 @@ defmodule Poxa.PusherEventTest do test "sending message to channels excluding a socket id" do expected = %{channel: "channel123", data: %{}, event: "event"} expect(JSX, :encode!, [{[expected], :msg}]) - expect(:gproc, :send, [{[{:p, :l, {:pusher, "channel123"}}, {self, :msg, "SocketId"}], :ok}]) + expect(:gproc, :send, [{[{:p, :l, {:pusher, "app_id", "channel123"}}, {self, :msg, "SocketId"}], :ok}]) - assert publish(%PusherEvent{data: %{}, channels: ["channel123"], name: "event", socket_id: "SocketId"}) == :ok + assert publish(%PusherEvent{app_id: "app_id", data: %{}, channels: ["channel123"], name: "event", socket_id: "SocketId"}) == :ok assert validate [:gproc, JSX] end diff --git a/test/subscription_test.exs b/test/subscription_test.exs index feb1feb..564ea55 100644 --- a/test/subscription_test.exs +++ b/test/subscription_test.exs @@ -18,40 +18,40 @@ defmodule Poxa.SubscriptionTest do end test "subscription to a public channel" do - expect(Channel, :subscribed?, 2, false) + expect(Channel, :subscribed?, 3, false) expect(:gproc, :reg, 1, :registered) - assert subscribe!(%{"channel" => "public-channel"}, nil) == {:ok, "public-channel"} + assert subscribe!("app_id", %{"channel" => "public-channel"}, nil) == {:ok, "public-channel"} assert validate :gproc end test "subscription to a missing channel name" do expect(PusherEvent, :pusher_error, 1, :error_message) - assert subscribe!(%{}, nil) == {:error, :error_message} + assert subscribe!("app_id", %{}, nil) == {:error, :error_message} assert validate PusherEvent end test "subscription to a public channel but already subscribed" do - expect(Channel, :subscribed?, 2, true) - assert subscribe!(%{"channel" => "public-channel"}, nil) == {:ok, "public-channel"} + expect(Channel, :subscribed?, 3, true) + assert subscribe!("app_id", %{"channel" => "public-channel"}, nil) == {:ok, "public-channel"} end test "subscription to a private channel" do - expect(Channel, :subscribed?, 2, false) + expect(Channel, :subscribed?, 3, false) expect(:gproc, :reg, 1, :registered) - expect(AuthSignature, :valid?, 2, true) - assert subscribe!(%{"channel" => "private-channel", + expect(AuthSignature, :valid?, 3, true) + assert subscribe!("app_id", %{"channel" => "private-channel", "auth" => "signeddata" }, "SocketId") == {:ok, "private-channel"} assert validate AuthSignature assert validate :gproc end test "subscription to a private channel having a channel_data" do - expect(Channel, :subscribed?, 2, false) + expect(Channel, :subscribed?, 3, false) expect(:gproc, :reg, 1, :registered) - expect(AuthSignature, :valid?, 2, true) - assert subscribe!(%{"channel" => "private-channel", + expect(AuthSignature, :valid?, 3, true) + assert subscribe!("app_id", %{"channel" => "private-channel", "auth" => "signeddata", "channel_data" => "{\"user_id\" : \"id123\", \"user_info\" : \"info456\"}"}, "SocketId") == {:ok, "private-channel"} assert validate AuthSignature @@ -59,12 +59,12 @@ defmodule Poxa.SubscriptionTest do end test "subscription to a presence channel" do - expect(Channel, :subscribed?, 2, false) + expect(Channel, :subscribed?, 3, false) expect(:gproc, :lookup_values, 1, :values) # subscribed? returns false - expect(AuthSignature, :valid?, 2, true) - expect(PresenceSubscription, :subscribe!, 2, :ok) + expect(AuthSignature, :valid?, 3, true) + expect(PresenceSubscription, :subscribe!, 3, :ok) - assert subscribe!(%{"channel" => "presence-channel", + assert subscribe!("app_id", %{"channel" => "presence-channel", "auth" => "signeddata", "channel_data" => "{\"user_id\" : \"id123\", \"user_info\" : \"info456\""}, "SocketId") == :ok @@ -74,10 +74,10 @@ defmodule Poxa.SubscriptionTest do end test "subscription to a private-channel having bad authentication" do - expect(AuthSignature, :valid?, 2, false) + expect(AuthSignature, :valid?, 3, false) expect(PusherEvent, :pusher_error, 1, :error_message) - assert subscribe!(%{"channel" => "private-channel", + assert subscribe!("app_id", %{"channel" => "private-channel", "auth" => "signeddate"}, "SocketId") == {:error, :error_message} assert validate AuthSignature @@ -85,10 +85,10 @@ defmodule Poxa.SubscriptionTest do end test "subscription to a presence-channel having bad authentication" do - expect(AuthSignature, :valid?, 2, false) + expect(AuthSignature, :valid?, 3, false) expect(PusherEvent, :pusher_error, 1, :error_message) - assert subscribe!(%{"channel" => "presence-channel", + assert subscribe!("app_id", %{"channel" => "presence-channel", "auth" => "signeddate"}, "SocketId") == {:error, :error_message} assert validate AuthSignature @@ -96,29 +96,29 @@ defmodule Poxa.SubscriptionTest do end test "unsubscribe channel being subscribed" do - expect(Channel, :subscribed?, 2, true) + expect(Channel, :subscribed?, 3, true) expect(Channel, :presence?, 1, false) expect(:gproc, :unreg, 1, :ok) - assert unsubscribe!(%{"channel" => "a_channel"}) == {:ok, "a_channel"} + assert unsubscribe!("app_id", %{"channel" => "a_channel"}) == {:ok, "a_channel"} assert validate :gproc end test "unsubscribe channel being not subscribed" do - expect(Channel, :subscribed?, 2, false) + expect(Channel, :subscribed?, 3, false) expect(Channel, :presence?, 1, false) - assert unsubscribe!(%{"channel" => "a_channel"}) == {:ok, "a_channel"} + assert unsubscribe!("app_id", %{"channel" => "a_channel"}) == {:ok, "a_channel"} end test "unsubscribe from a presence channel" do - expect(Channel, :subscribed?, 2, false) + expect(Channel, :subscribed?, 3, false) expect(Channel, :presence?, 1, true) expect(:gproc, :unreg, 1, :ok) - expect(PresenceSubscription, :unsubscribe!, 1, {:ok, "presence-channel"}) + expect(PresenceSubscription, :unsubscribe!, 2, {:ok, "presence-channel"}) - assert unsubscribe!(%{"channel" => "presence-channel"}) == {:ok, "presence-channel"} + assert unsubscribe!("app_id", %{"channel" => "presence-channel"}) == {:ok, "presence-channel"} assert validate PresenceSubscription assert validate :gproc diff --git a/test/test_helper.exs b/test/test_helper.exs index 0211338..68dbe29 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -12,10 +12,10 @@ defmodule Connection do end defmodule SpawnHelper do - def spawn_registered_process(channel, value \\ :undefined) do + def spawn_registered_process(app_id, channel, value \\ :undefined) do parent = self spawn_link fn -> - register_to_channel(channel, value) + register_to_channel(app_id, channel, value) send parent, :registered receive do _ -> :wait @@ -26,7 +26,7 @@ defmodule SpawnHelper do end end - def register_to_channel(channel, value \\ :undefined) do - :gproc.reg({:p, :l, {:pusher, channel}}, value) + def register_to_channel(app_id, channel, value \\ :undefined) do + :gproc.reg({:p, :l, {:pusher, app_id, channel}}, value) end end diff --git a/test/users_handler_test.ex b/test/users_handler_test.exs similarity index 88% rename from test/users_handler_test.ex rename to test/users_handler_test.exs index 0e97680..c571bd5 100644 --- a/test/users_handler_test.ex +++ b/test/users_handler_test.exs @@ -30,7 +30,8 @@ defmodule Poxa.UsersHandlerTest do end test "get_json returns users of a presence channel" do - expect(PresenceChannel, :users, 1, ["user1", "user2"]) + expect(:cowboy_req, :binding, 2, {"app_id", :req}) + expect(PresenceChannel, :users, [{["channel123", "app_id"], ["user1", "user2"]}]) expected = [users: [[id: "user1"], [id: "user2"]]] expect(JSX, :encode!, [{[expected], :encoded_json}]) diff --git a/test/websocket_handler_test.exs b/test/websocket_handler_test.exs index eb35da6..10652a5 100644 --- a/test/websocket_handler_test.exs +++ b/test/websocket_handler_test.exs @@ -1,5 +1,6 @@ defmodule Poxa.WebsocketHandlerTest do use ExUnit.Case + alias Poxa.App alias Poxa.PresenceSubscription alias Poxa.PusherEvent alias Poxa.Subscription @@ -11,12 +12,6 @@ defmodule Poxa.WebsocketHandlerTest do import Poxa.WebsocketHandler alias Poxa.WebsocketHandler.State - setup_all do - :application.set_env(:poxa, :app_secret, "secret") - :application.set_env(:poxa, :app_key, "app_key") - :ok - end - setup do on_exit fn -> unload end :ok @@ -45,11 +40,12 @@ defmodule Poxa.WebsocketHandlerTest do expect(:cowboy_req, :host_url, 1, {:host_url, :req2}) expect(PusherEvent, :connection_established, 1, :connection_established) expect(SocketId, :generate!, 0, "123.456") - expect(Event, :notify, [{[:connected, %{socket_id: "123.456", origin: :host_url}], :ok}]) + expect(Event, :notify, [{[:connected, 123, %{socket_id: "123.456", origin: :host_url}], :ok}]) expect(Time, :stamp, 0, 123) - assert websocket_info(:start, :req, :state) == - {:reply, {:text, :connection_established}, :req2, %State{socket_id: "123.456", time: 123}} + assert websocket_info(:start, :req, %State{app_id: 123}) == + {:reply, {:text, :connection_established}, :req2, + %State{app_id: 123, socket_id: "123.456", time: 123}} assert validate [PusherEvent, Event, SocketId] end @@ -86,11 +82,11 @@ defmodule Poxa.WebsocketHandlerTest do test "subscribe private channel event" do expect(JSX, :decode!, 1, %{"event" => "pusher:subscribe"}) - expect(Subscription, :subscribe!, 2, {:ok, :channel}) + expect(Subscription, :subscribe!, 3, {:ok, :channel}) expect(PusherEvent, :subscription_succeeded, 1, :subscription_succeeded) - expect(Event, :notify, [{[:subscribed, %{socket_id: :socket_id, channel: :channel}], :ok}]) + expect(Event, :notify, [{[:subscribed, :app_id, %{socket_id: :socket_id, channel: :channel}], :ok}]) - state = %State{socket_id: :socket_id} + state = %State{socket_id: :socket_id, app_id: :app_id} assert websocket_handle({:text, :subscribe_json}, :req, state) == {:reply, {:text, :subscription_succeeded}, :req, state} @@ -100,7 +96,7 @@ defmodule Poxa.WebsocketHandlerTest do test "subscribe private channel event failing for bad authentication" do expect(JSX, :decode!, 1, %{"event" => "pusher:subscribe"}) - expect(Subscription, :subscribe!, 2, {:error, :error_message}) + expect(Subscription, :subscribe!, 3, {:error, :error_message}) state = %State{socket_id: :socket_id} @@ -112,11 +108,11 @@ defmodule Poxa.WebsocketHandlerTest do test "subscribe presence channel event" do expect(JSX, :decode!, 1, %{"event" => "pusher:subscribe"}) - expect(Subscription, :subscribe!, 2, %PresenceSubscription{channel: :channel}) + expect(Subscription, :subscribe!, 3, %PresenceSubscription{channel: :channel}) expect(PusherEvent, :subscription_succeeded, 1, :subscription_succeeded) - expect(Event, :notify, [{[:subscribed, %{socket_id: :socket_id, channel: :channel}], :ok}]) + expect(Event, :notify, [{[:subscribed, :app_id, %{socket_id: :socket_id, channel: :channel}], :ok}]) - state = %State{socket_id: :socket_id} + state = %State{socket_id: :socket_id, app_id: :app_id} assert websocket_handle({:text, :subscribe_json}, :req, state) == {:reply, {:text, :subscription_succeeded}, :req, state} @@ -126,7 +122,7 @@ defmodule Poxa.WebsocketHandlerTest do test "subscribe presence channel event failing for bad authentication" do expect(JSX, :decode!, 1, %{"event" => "pusher:subscribe"}) - expect(Subscription, :subscribe!, 2, {:error, :error_message}) + expect(Subscription, :subscribe!, 3, {:error, :error_message}) state = %State{socket_id: :socket_id} @@ -138,11 +134,11 @@ defmodule Poxa.WebsocketHandlerTest do test "subscribe public channel event" do expect(JSX, :decode!, 1, %{"event" => "pusher:subscribe"}) - expect(Subscription, :subscribe!, 2, {:ok, :channel}) + expect(Subscription, :subscribe!, 3, {:ok, :channel}) expect(PusherEvent, :subscription_succeeded, 1, :subscription_succeeded) - expect(Event, :notify, [{[:subscribed, %{socket_id: :socket_id, channel: :channel}], :ok}]) + expect(Event, :notify, [{[:subscribed, :app_id, %{socket_id: :socket_id, channel: :channel}], :ok}]) - state = %State{socket_id: :socket_id} + state = %State{socket_id: :socket_id, app_id: :app_id} assert websocket_handle({:text, :subscribe_json}, :req, state) == {:reply, {:text, :subscription_succeeded}, :req, state} @@ -152,11 +148,11 @@ defmodule Poxa.WebsocketHandlerTest do test "subscribe event on an already subscribed channel" do expect(JSX, :decode!, 1, %{"event" => "pusher:subscribe"}) - expect(Subscription, :subscribe!, 2, {:ok, :channel}) + expect(Subscription, :subscribe!, 3, {:ok, :channel}) expect(PusherEvent, :subscription_succeeded, 1, :subscription_succeeded) - expect(Event, :notify, [{[:subscribed, %{socket_id: :socket_id, channel: :channel}], :ok}]) + expect(Event, :notify, [{[:subscribed, :app_id, %{socket_id: :socket_id, channel: :channel}], :ok}]) - state = %State{socket_id: :socket_id} + state = %State{socket_id: :socket_id, app_id: :app_id} assert websocket_handle({:text, :subscribe_json}, :req, state) == {:reply, {:text, :subscription_succeeded}, :req, state} @@ -166,10 +162,10 @@ defmodule Poxa.WebsocketHandlerTest do test "unsubscribe event" do expect(JSX, :decode!, 1, %{"event" => "pusher:unsubscribe", "data" => :data}) - expect(Subscription, :unsubscribe!, [{[:data], {:ok, :channel}}]) - expect(Event, :notify, [{[:unsubscribed, %{socket_id: :socket_id, channel: :channel}], :ok}]) + expect(Subscription, :unsubscribe!, [{["app_id", :data], {:ok, :channel}}]) + expect(Event, :notify, [{[:unsubscribed, "app_id", %{socket_id: :socket_id, channel: :channel}], :ok}]) - state = %State{socket_id: :socket_id} + state = %State{socket_id: :socket_id, app_id: "app_id"} assert websocket_handle({:text, :unsubscribe_json}, :req, state) == {:ok, :req, state} @@ -181,12 +177,12 @@ defmodule Poxa.WebsocketHandlerTest do decoded_json = %{"event" => "client-event"} event = %PusherEvent{channels: ["presence-channel"], name: "client-event"} expect(JSX, :decode!, [{[:client_event_json], decoded_json}]) - expect(PusherEvent, :build_client_event, [{[decoded_json, :socket_id], {:ok, event}}]) + expect(PusherEvent, :build_client_event, [{["app_id", decoded_json, :socket_id], {:ok, event}}]) expect(PusherEvent, :publish, 1, :ok) - expect(Channel, :subscribed?, 2, true) - expect(Event, :notify, [{[:client_event_message, %{socket_id: :socket_id, channels: ["presence-channel"], name: "client-event"}], :ok}]) + expect(Channel, :subscribed?, 3, true) + expect(Event, :notify, [{[:client_event_message, "app_id", %{socket_id: :socket_id, channels: ["presence-channel"], name: "client-event"}], :ok}]) - state = %State{socket_id: :socket_id} + state = %State{socket_id: :socket_id, app_id: "app_id"} assert websocket_handle({:text, :client_event_json}, :req, state) == {:ok, :req, state} @@ -198,12 +194,12 @@ defmodule Poxa.WebsocketHandlerTest do decoded_json = %{"event" => "client-event"} event = %PusherEvent{channels: ["private-channel"], name: "client-event"} expect(JSX, :decode!, [{[:client_event_json], decoded_json}]) - expect(PusherEvent, :build_client_event, [{[decoded_json, :socket_id], {:ok, event}}]) + expect(PusherEvent, :build_client_event, [{["app_id", decoded_json, :socket_id], {:ok, event}}]) expect(PusherEvent, :publish, 1, :ok) - expect(Channel, :subscribed?, 2, true) - expect(Event, :notify, [{[:client_event_message, %{socket_id: :socket_id, channels: ["private-channel"], name: "client-event"}], :ok}]) + expect(Channel, :subscribed?, 3, true) + expect(Event, :notify, [{[:client_event_message, "app_id", %{socket_id: :socket_id, channels: ["private-channel"], name: "client-event"}], :ok}]) - state = %State{socket_id: :socket_id} + state = %State{socket_id: :socket_id, app_id: "app_id"} assert websocket_handle({:text, :client_event_json}, :req, state) == {:ok, :req, state} @@ -215,10 +211,10 @@ defmodule Poxa.WebsocketHandlerTest do decoded_json = %{"event" => "client-event"} event = %PusherEvent{channels: ["private-not-subscribed"], name: "client-event"} expect(JSX, :decode!, [{[:client_event_json], decoded_json}]) - expect(PusherEvent, :build_client_event, [{[decoded_json, :socket_id], {:ok, event}}]) - expect(Channel, :subscribed?, 2, false) + expect(PusherEvent, :build_client_event, [{["app_id", decoded_json, :socket_id], {:ok, event}}]) + expect(Channel, :subscribed?, 3, false) - state = %State{socket_id: :socket_id} + state = %State{socket_id: :socket_id, app_id: "app_id"} assert websocket_handle({:text, :client_event_json}, :req, state) == {:ok, :req, state} @@ -229,7 +225,7 @@ defmodule Poxa.WebsocketHandlerTest do test "client event on public channel" do event = %PusherEvent{channels: ["public-channel"], name: "client-event"} expect(JSX, :decode!, 1, %{"event" => "client-event"}) - expect(PusherEvent, :build_client_event, 2, {:ok, event}) + expect(PusherEvent, :build_client_event, 3, {:ok, event}) state = %State{socket_id: :socket_id} @@ -243,56 +239,64 @@ defmodule Poxa.WebsocketHandlerTest do expect(:cowboy_req, :binding, 2, {"app_key", :req}) expect(:cowboy_req, :qs_val, 3, {"7", :req}) expect(PusherEvent, :connection_established, 1, :connection_established) + expect(App, :id, [{["app_key"], {:ok, "app_id"}}]) + + state = %State{app_id: "app_id"} - assert websocket_init(:transport, :req, :opts) == {:ok, :req, nil} + assert websocket_init(:transport, :req, :opts) == {:ok, :req, state} - assert validate [:cowboy_req, PusherEvent] + assert validate [:cowboy_req, PusherEvent, App] end test "websocket init using wrong app_key" do expect(:cowboy_req, :binding, 2, {"different_app_key", :req}) expect(:cowboy_req, :qs_val, 3, {"7", :req}) + expect(App, :id, [{["different_app_key"], {:error, :reason}}]) assert websocket_init(:transport, :req, :opts) == {:ok, :req, {4001, "Application does not exist"}} - assert validate :cowboy_req + assert validate [:cowboy_req, App] end test "websocket init using protocol higher than 7" do expect(:cowboy_req, :binding, 2, {"app_key", :req}) expect(:cowboy_req, :qs_val, 3, {"8", :req}) + expect(App, :id, [{["app_key"], {:ok, "app_id"}}]) assert websocket_init(:transport, :req, :opts) == {:ok, :req, {4007, "Unsupported protocol version"}} - assert validate :cowboy_req + assert validate [:cowboy_req, App] end test "websocket init using protocol lower than 5" do expect(:cowboy_req, :binding, 2, {"app_key", :req}) expect(:cowboy_req, :qs_val, 3, {"4", :req}) + expect(App, :id, [{["app_key"], {:ok, "app_id"}}]) assert websocket_init(:transport, :req, :opts) == {:ok, :req, {4007, "Unsupported protocol version"}} - assert validate :cowboy_req + assert validate [:cowboy_req, App] end test "websocket init using protocol between 5 and 7" do expect(:cowboy_req, :binding, 2, {"app_key", :req}) expect(:cowboy_req, :qs_val, 3, {"6", :req}) + expect(App, :id, [{["app_key"], {:ok, "app_id"}}]) - assert websocket_init(:transport, :req, :opts) == {:ok, :req, nil} + state = %State{app_id: "app_id"} + assert websocket_init(:transport, :req, :opts) == {:ok, :req, state} - assert validate :cowboy_req + assert validate [:cowboy_req, App] end test "websocket termination" do - expect(Channel, :all, 1, :channels) + expect(Channel, :all, 2, :channels) expect(Time, :stamp, 0, 100) - expect(Event, :notify, [{[:disconnected, %{socket_id: :socket_id, channels: :channels, duration: 90}], :ok}]) - expect(PresenceSubscription, :check_and_remove, 0, :ok) + expect(Event, :notify, [{[:disconnected, :app_id, %{socket_id: :socket_id, channels: :channels, duration: 90}], :ok}]) + expect(PresenceSubscription, :check_and_remove, 1, :ok) expect(:gproc, :goodbye, 0, :ok) - state = %State{socket_id: :socket_id, time: 10} + state = %State{socket_id: :socket_id, time: 10, app_id: :app_id} assert websocket_terminate(:reason, :req, state) == :ok