danschultzer / phoenix_oauth2_provider

Get an OAuth 2 provider running in your phoenix with controllers, views and models in just two minutes
MIT License
84 stars 41 forks source link

Testing API Controller? #25

Closed kitplummer closed 5 years ago

kitplummer commented 5 years ago

Ok, next question. :) Trying to get my API controller tests to pass. Obviously my user fixture is "Unauthenticated". But, I'm not quite sure how to make that so. Again sorry to bug, but am hopeful a lil help will get me moving again. Is there something I can 'set' in the fixture? Inspecting the conn value isn't overly helpful as I don't see something that would indicated "authenticated". Realize also that these are kinda just question, and not sure if there is a better forum for asking.

kitplummer commented 5 years ago

I think I've found my solution...stealing from this repo's controller tests. Just need to setup the fixtures for application, access_token, and access_grant.

danschultzer commented 5 years ago

Great! Just to write it out for others who may find this issue, you can use the ExOauth2Provider.Plug.set_current_access_token/1 to set the access token for the conn.

Could look like this:

ExOauth2Provider.Plug.set_current_access_token(conn, {:ok, access_token})`

Alternatively, you can set the auth header, and let your pipeline deal with auth:

Plug.Conn.put_req_header(conn, "authorization", "Bearer #{access_token.token}")
kitplummer commented 5 years ago

Well...I was a little too optimistic. Think I missing something in the setup of the user. Any ideas?

Test case:

defmodule LeiApiWeb.API.V1.UserControllerTest do
  use LeiApiWeb.ConnCase

  setup %{conn: conn} do
    user = user_fixture()
    conn = assign(conn, :current_user, user)
    application = application(%{user: user, redirect_uri: "urn:ietf:wg:oauth:2.0:oob"})
    access_token = access_grant(%{application: application, user: user})
    #ExOauth2Provider.Plug.set_current_access_token(conn, access_token)
    conn = Plug.Conn.put_req_header(conn, "authorization", access_token.token)
    conn = Plug.Conn.put_req_header(conn, "accept", "application/json")
    {:ok, conn: conn}

  end

  describe "index" do
    test "lists all users", %{conn: conn} do
      conn = get(conn, Routes.user_path(conn, :index))
      IO.inspect conn
      assert json_response(conn, 200)["data"] == []
    end
  end
end

Output:

..................%Plug.Conn{
  adapter: {Plug.Adapters.Test.Conn, :...},
  assigns: %{
    current_user: %LeiApi.Users.User{
      __meta__: #Ecto.Schema.Metadata<:loaded, "users">,
      confirm_password: "abcdefghijk",
      current_password: nil,
      email: "bob963@example.com",
      id: 270,
      inserted_at: ~N[2019-08-06 21:02:53],
      password: "abcdefghijk",
      password_hash: "$pbkdf2-sha512$100000$J1172bf8L9VGCkjqBkF6HQ==$e5noOYKW74KtXYJyUs0kn4DHP/UD106YoIejNOO9LNIA2qXrbTuNNUIW95vkUzKEnOXfra1+v53w04Kx2xZL2A==",
      queries: #Ecto.Association.NotLoaded<association :queries is not loaded>,
      updated_at: ~N[2019-08-06 21:02:53]
    },
    ex_oauth2_provider_failure: :no_session
  },
  before_send: [#Function<0.66442134/1 in Plug.Telemetry.call/2>],
  body_params: %{},
  cookies: %Plug.Conn.Unfetched{aspect: :cookies},
  halted: true,
  host: "www.example.com",
  method: "GET",
  owner: #PID<0.418.0>,
  params: %{},
  path_info: ["api", "v1", "accounts"],
  path_params: %{},
  port: 80,
  private: %{
    LeiApiWeb.Router => {[], %{}},
    :phoenix_endpoint => LeiApiWeb.Endpoint,
    :phoenix_format => "json",
    :phoenix_recycled => false,
    :phoenix_router => LeiApiWeb.Router,
    :plug_session_fetch => #Function<1.35161479/1 in Plug.Session.fetch_session/1>,
    :plug_skip_csrf_protection => true,
    :pow_config => [
      mod: Pow.Plug.Session,
      plug: Pow.Plug.Session,
      otp_app: :lei_api
    ]
  },
  query_params: %{},
  query_string: "",
  remote_ip: {127, 0, 0, 1},
  req_cookies: %Plug.Conn.Unfetched{aspect: :cookies},
  req_headers: [
    {"authorization",
     "6c53cce675fe8ed47b16f6794245dbda5cd4e0044b63c4fb17b1471360acff10"},
    {"accept", "application/json"}
  ],
  request_path: "/api/v1/accounts",
  resp_body: "{\"errors\":[\"Unauthenticated\"]}",
  resp_cookies: %{},
  resp_headers: [
    {"cache-control", "max-age=0, private, must-revalidate"},
    {"x-request-id", "FbhxPBqBduA2438AAAGB"},
    {"content-type", "application/json; charset=utf-8"}
  ],
  scheme: :http,
  script_name: [],
  secret_key_base: :...,
  state: :sent,
  status: 401
}

  1) test index lists all users (LeiApiWeb.API.V1.UserControllerTest)
     test/lei_api_web/controllers/api/v1/user_controller_test.exs:17
     ** (RuntimeError) expected response with status 200, got: 401, with body:
     {"errors":["Unauthenticated"]}
     code: assert json_response(conn, 200)["data"] == []
     stacktrace:
       (phoenix) lib/phoenix/test/conn_test.ex:373: Phoenix.ConnTest.response/2
       (phoenix) lib/phoenix/test/conn_test.ex:419: Phoenix.ConnTest.json_response/2
       test/lei_api_web/controllers/api/v1/user_controller_test.exs:20: (test)

.

Finished in 2.9 seconds
20 tests, 1 failure
danschultzer commented 5 years ago

Oh, use this instead:

Plug.Conn.put_req_header(conn, "authorization", "Bearer #{access_token.token}")

I forgot that you used the bearer realm for the plug (which is what's used for the examples as well). I'll updated the above example as well.

kitplummer commented 5 years ago

Thanks. I did try that...and the header is received with the "Bearer" prefix. But still getting the 401 in the test.

But now I get ex_oauth2_provider_failure: :token_not_found:

..................%Plug.Conn{
  adapter: {Plug.Adapters.Test.Conn, :...},
  assigns: %{
    current_user: %LeiApi.Users.User{
      __meta__: #Ecto.Schema.Metadata<:loaded, "users">,
      confirm_password: "abcdefghijk",
      current_password: nil,
      email: "bob867@example.com",
      id: 288,
      inserted_at: ~N[2019-08-07 13:15:56],
      password: "abcdefghijk",
      password_hash: "$pbkdf2-sha512$100000$UALQQwh7fCftb6X7/H49lg==$DBfCFV6vG4Y2ymu/TQvDR9W5APoyp9+5KNDUWGQ585wDryCMC5HABlm6IFuzGMfAMvuUhRCw1sjHfQcxgljXjw==",
      queries: #Ecto.Association.NotLoaded<association :queries is not loaded>,
      updated_at: ~N[2019-08-07 13:15:56]
    },
    ex_oauth2_provider_failure: :token_not_found
  },
danschultzer commented 5 years ago

What does access_grant do? You'll need to set up an access token, sounds like you just set up the grant.

kitplummer commented 5 years ago

Doh, in my troubleshooting of things I forgot to change it back to access_token from access_grant after adding the "Bearer" prefix. Doing so, has the auth pipeline working in my controller test. Awesome...thanks!