pow-auth / pow_assent

Multi-provider authentication for your Pow enabled app
https://powauth.com
MIT License
321 stars 50 forks source link

Is it possible to mock out a valid user for acceptance tests with Wallaby / Hound? #188

Open asurin opened 4 years ago

asurin commented 4 years ago

I'm currently working on switching an app that uses Samly out to use pow/pow_assent. It currently has a large body of acceptance tests using Wallaby. With Samly, the assertion manager can essentially be entirely swapped out, making it fairly simple for us to use mocks to simulate a variety of connection states.

Is there a recommended way to do something similar here? Pretty much everything I've come up with involves basically removing the auth library for the test env and injecting a user manually into session but this is super fragile and makes a lot of code messy!

edit: To clarify, it's not too hard to create a dummy plug that sets :current_user in the assigns and fools the pages(s) into thinking a user's in session - Right now to have the app behave correctly on the DB-side I'm also having to basically reconstruct the upsert functionality in pow_assert in the aforementioned, which is what seems messy/fragile as I'm basically mirroring portions of package in my own test support file

danschultzer commented 4 years ago

I would either mock the strategy or the http adapter .

Normally I mock the http adapter:

defmodule MockHTTPAdapter do
  @moduledoc false

  alias Assent.HTTPAdapter.HTTPResponse

  def request(:post, "http://example.com/oauth/access_token", body, _headers, nil) do
    case URI.decode_query(body) do
      %{"code" => "valid_" <> token} ->
        {:ok, %HTTPResponse{status: 200, body: %{"access_token" => token}}}

      _any ->
        {:ok, %HTTPResponse{status: 401, body: ""}}
    end
  end

  def request(:get, "http://example.com/user", _body, _header, nil) do
    {:ok, %HTTPResponse{status: 200, body: %{id: 1}}}
  end
end

Then you can add it to the test config:

config :my_app, :pow_assent,
  http_adapter: MockHTTPAdapter

The above is OAuth2, so to test the flow I check if the code in params returned starts with valid_. Makes it very easy to test. Often with ExUnit I add the module in the test modules and set the application env in a setup callback.

Alternatively you can mock the strategy:

defmodule TwitterMock do
  @moduledoc false
  @behaviour Assent.Strategy

  @impl true
  def authorize_url(_config),
    do: {:ok, %{url: "https://provider.example.com/oauth/authorize", session_params: %{a: 1}}}

  @impl true
  def callback(_config, %{"code" => "valid"}), do: {:ok, %{user: %{"sub" => 1, "email" => "test@example.com", "name" => "John Doe"}, token: %{"access_token" => "access_token"}}}
  def callback(_config, _params), do: {:error, "Invalid params"}
end
config :my_app, :pow_assent,
  providers: [
    twitter: [strategy: TwitterMock]
  ]

In PowAssent I use bypass with a test provider: https://github.com/pow-auth/pow_assent/blob/master/test/support/test_provider.ex

asurin commented 4 years ago

This is awesome feedback - very much appreciated. It sounds like mocking the HTTP adapter might be the best way to proceed - I tried mocking the strategy but I think the system still hits up the URL in question.

Would it be helpful if I opened a PR with some guide examples based on this? If so happy to pay the time you spent writing this back with one!

danschultzer commented 4 years ago

Would it be helpful if I opened a PR with some guide examples based on this?

Yeah it would! 😄