Last active
October 26, 2023 00:05
-
-
Save mmcc/b8768508181859a8618a162ac03d7b2a to your computer and use it in GitHub Desktop.
Elixir test macros
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule AppWeb.AuthConnCase do | |
alias AppWeb.Router | |
use ExUnit.CaseTemplate | |
import Phoenix.ConnTest, only: [dispatch: 5, json_response: 2] | |
@doc """ | |
Allows you to call the same set of tests with the same describe block | |
Setups is an array of named setups, i.e. [first_setup: [:setup_conn, :thing], second_setup: [:different_setup, :neato_setup]] | |
## Examples | |
defmodule MultiAuthTest do | |
use ExUnit.Case, async: true | |
describe_multi "Something works with both cookies and api keys", | |
[cookie_auth: [:create_user, :cookie_authed_connection], | |
token_auth: [:create_api_key, :api_key_authed_connection]] do | |
test "authed conn is authed", %{conn: conn} do | |
# ... | |
end | |
end | |
end | |
""" | |
defmacro describe_multi(message, setups, do: block) do | |
for {name, setup} <- setups do | |
quote do | |
describe "#{unquote(message)} (#{unquote(name)})" do | |
setup unquote(setup) | |
unquote(block) | |
end | |
end | |
end | |
end | |
@doc """ | |
Simple macros to test whether or not a route is authenticated. | |
## Examples | |
defmodule AuthenticatedTest do | |
use ExUnit.Case, async: true | |
@path_helper :authenticated_path | |
test_authentication_required_for(:get, @path_helper, :index, [], interface_only: true) | |
test_authentication_required_for(:post, @path_helper, :create, []) | |
test_authentication_required_for(:delete, @path_helper, :delete, [123]) | |
end | |
""" | |
defmacro test_authentication_required_for(method, path_name, action, additional_path_args \\ [], options \\ []) do | |
%{interface_only: interface_only} = Enum.into(options, %{interface_only: false}) | |
auth_test = quote do | |
test "#{unquote(action)} requires authentication", %{conn: conn} do | |
method = unquote(method) | |
path_name = unquote(path_name) | |
action = unquote(action) | |
args = unquote(additional_path_args) | |
options = unquote(options) | |
assert make_unauthenticated_request(conn, @endpoint, method, path_name, action, args) | |
end | |
end | |
interface_only_test = interface_only and quote do | |
test "#{unquote(action)} requires cookie authentication", %{conn: conn} do | |
method = unquote(method) | |
path_name = unquote(path_name) | |
action = unquote(action) | |
args = unquote(additional_path_args) | |
options = unquote(options) | |
assert make_access_token_authenticated_request(conn, @endpoint, method, path_name, action, args) | |
end | |
end | |
[auth_test, interface_only_test] | |
end | |
def make_unauthenticated_request(conn, endpoint, method, path_name, action, additional_args) do | |
args = [conn, action] ++ additional_args | |
path = apply(Router.Helpers, path_name, args) | |
dispatch(conn, endpoint, method, path, nil) |> json_response(401) | |
end | |
def make_access_token_authenticated_request(conn, endpoint, method, path_name, action, additional_args) do | |
args = [conn, action] ++ additional_args | |
user = Factory(:user) | |
token_params = %{ | |
name: "Some Key", | |
permissions: ["data:read", "video:read", "video:write"] | |
} | |
{:ok, token} = AppWeb.User.create_access_token(user, token_params) | |
auth_string = "#{token.id}:#{token.private_key}" |> Base.encode64 | |
conn = Plug.Conn.put_req_header(conn, "authorization", "Basic #{auth_string}") | |
path = apply(Router.Helpers, path_name, args) | |
resp = dispatch(conn, endpoint, method, path, nil) |> json_response(401) | |
resp["error"]["messages"] |> hd() =~ "dashboard interface" | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment