From da9f98b3c6dfec8bea445212987ee4be2d3538e7 Mon Sep 17 00:00:00 2001 From: Klemen Sever Date: Thu, 18 Jun 2020 09:21:17 +0200 Subject: [PATCH] chore(tests): Adds tests to the lib --- .travis.yml | 12 +- config/config.exs | 1 + config/test.exs | 17 ++ mix.exs | 6 +- test/fixtures/auth0.json | 9 + test/fixtures/auth0_response.html | 1 + .../vcr_cassettes/auth0-invalid-code.json | 40 ++++ .../vcr_cassettes/auth0-no-access-token.json | 39 ++++ .../vcr_cassettes/auth0-responses.json | 57 ++++++ test/strategy/auth0/oauth_test.exs | 21 ++ test/strategy/auth0_test.exs | 189 ++++++++++++++++++ test/test_helper.exs | 21 ++ test/ueberauth_auth0_test.exs | 8 - 13 files changed, 406 insertions(+), 15 deletions(-) create mode 100644 config/test.exs create mode 100644 test/fixtures/auth0.json create mode 100644 test/fixtures/auth0_response.html create mode 100644 test/fixtures/vcr_cassettes/auth0-invalid-code.json create mode 100644 test/fixtures/vcr_cassettes/auth0-no-access-token.json create mode 100644 test/fixtures/vcr_cassettes/auth0-responses.json create mode 100644 test/strategy/auth0/oauth_test.exs create mode 100644 test/strategy/auth0_test.exs delete mode 100644 test/ueberauth_auth0_test.exs diff --git a/.travis.yml b/.travis.yml index d370591..531b87c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,12 +38,12 @@ matrix: otp_release: '21.3' - elixir: '1.9' otp_release: '22.0' -# - elixir: '1.10' -# otp_release: '20.3' -# - elixir: '1.10' -# otp_release: '21.3' -# - elixir: '1.10' -# otp_release: '22.0' + - elixir: '1.10' + otp_release: '20.3' + - elixir: '1.10' + otp_release: '21.3' + - elixir: '1.10' + otp_release: '22.0' env: global: diff --git a/config/config.exs b/config/config.exs index 00622fe..69c67b3 100644 --- a/config/config.exs +++ b/config/config.exs @@ -28,3 +28,4 @@ use Mix.Config # here (which is why it is important to import them last). # # import_config "#{Mix.env}.exs" +if Mix.env() == :test, do: import_config("test.exs") diff --git a/config/test.exs b/config/test.exs new file mode 100644 index 0000000..da155c7 --- /dev/null +++ b/config/test.exs @@ -0,0 +1,17 @@ +use Mix.Config + +config :ueberauth, Ueberauth, + json_library: Jason, + providers: [ + auth0: {Ueberauth.Strategy.Auth0, []} + ] + +config :ueberauth, Ueberauth.Strategy.Auth0.OAuth, + domain: "example-app.auth0.com", + client_id: "clientidsomethingrandom", + client_secret: "clientsecret-somethingsecret" + +config :exvcr, + vcr_cassette_library_dir: "test/fixtures/vcr_cassettes" + +config :plug, :validate_header_keys_during_test, true diff --git a/mix.exs b/mix.exs index e0992c0..f74a4b9 100644 --- a/mix.exs +++ b/mix.exs @@ -24,7 +24,11 @@ defmodule UeberauthAuth0.Mixfile do coveralls: :test, "coveralls.detail": :test, "coveralls.post": :test, - "coveralls.html": :test + "coveralls.html": :test, + vcr: :test, + "vcr.delete": :test, + "vcr.check": :test, + "vcr.show": :test ], # Type checking diff --git a/test/fixtures/auth0.json b/test/fixtures/auth0.json new file mode 100644 index 0000000..467137c --- /dev/null +++ b/test/fixtures/auth0.json @@ -0,0 +1,9 @@ +{ + "email": "testuser@example.com", + "email_verified": false, + "name": "testuser@example.com", + "nickname": "testuser", + "picture": "https://s.gravatar.com/avatar/7ec7606c46a14a7ef514d1f1f9038823?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Ftu.png", + "sub": "auth0|lyy5vutbn9qfmihil7pvpo66", + "updated_at": "2020-02-19T12:34:56.784Z" +} diff --git a/test/fixtures/auth0_response.html b/test/fixtures/auth0_response.html new file mode 100644 index 0000000..2e83a93 --- /dev/null +++ b/test/fixtures/auth0_response.html @@ -0,0 +1 @@ +You are being redirected. diff --git a/test/fixtures/vcr_cassettes/auth0-invalid-code.json b/test/fixtures/vcr_cassettes/auth0-invalid-code.json new file mode 100644 index 0000000..3d50edf --- /dev/null +++ b/test/fixtures/vcr_cassettes/auth0-invalid-code.json @@ -0,0 +1,40 @@ +[ + { + "request": { + "body": "client_id=clientidKFpqmR7aO1iVUOQR1LMKbZvk&client_secret=clientsecrethzqAhvWpJhAehfkzupWelgut-hRh7ZYOBOmaXsCRryhy7bqWzOCx&code=invalid_code&grant_type=authorization_code&redirect_uri=http%3A%2F%2Fwww.example.com%2Fauth%2Fauth0%2Fcallback", + "headers": { + "content-type": "application/x-www-form-urlencoded", + "accept": "application/json", + "authorization": "Basic Y2xpZW50aWRLRnBxbVI3YU8xaVZVT1FSMUxNS2Jadms6Y2xpZW50c2VjcmV0aHpxQWh2V3BKaEFlaGZrenVwV2VsZ3V0LWhSaDdaWU9CT21hWHNDUnJ5aHk3YnFXek9DeA==" + }, + "method": "post", + "options": [], + "request_body": "", + "url": "https://example-app.auth0.com/oauth/token" + }, + "response": { + "binary": false, + "body": "{\"error\":\"invalid_grant\",\"error_description\":\"Invalid authorization code\"}", + "headers": { + "Date": "Thu, 18 Jun 2020 07:03:26 GMT", + "Content-Type": "application/json", + "Content-Length": "74", + "Connection": "keep-alive", + "Server": "nginx", + "Vary": "Accept-Encoding", + "ot-tracer-spanid": "5f4018ed528710ca", + "ot-tracer-traceid": "1e9ed8bd1011f221", + "ot-tracer-sampled": "true", + "ot-baggage-auth0-request-id": "b105f9bcbad6f804b763ee88", + "X-Auth0-RequestId": "aedc633e5adea3be8f49", + "Set-Cookie": "did=s%3Av0%3Ace5ff350-b131-11ea-be83-892b118656b2.%2B9lyU8P6x2BabrexQ7tK2sK19C2RkOlnda890p8d9Z0; Max-Age=31557600; Path=/; Expires=Fri, 18 Jun 2021 13:03:26 GMT; HttpOnly; Secure; SameSite=None", + "X-RateLimit-Limit": "30", + "X-RateLimit-Remaining": "29", + "X-RateLimit-Reset": "1592463807", + "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, no-transform" + }, + "status_code": 403, + "type": "ok" + } + } +] diff --git a/test/fixtures/vcr_cassettes/auth0-no-access-token.json b/test/fixtures/vcr_cassettes/auth0-no-access-token.json new file mode 100644 index 0000000..a219531 --- /dev/null +++ b/test/fixtures/vcr_cassettes/auth0-no-access-token.json @@ -0,0 +1,39 @@ +[ + { + "request": { + "body": "client_id=clientidKFpqmR7aO1iVUOQR1LMKbZvk&client_secret=clientsecrethzqAhvWpJhAehfkzupWelgut-hRh7ZYOBOmaXsCRryhy7bqWzOCx&code=code_abc&grant_type=authorization_code&redirect_uri=http%3A%2F%2Fwww.example.com%2Fauth%2Fauth0%2Fcallback", + "headers": { + "content-type": "application/x-www-form-urlencoded", + "accept": "application/json", + "authorization": "Basic Y2xpZW50aWRLRnBxbVI3YU8xaVZVT1FSMUxNS2Jadms6Y2xpZW50c2VjcmV0aHpxQWh2V3BKaEFlaGZrenVwV2VsZ3V0LWhSaDdaWU9CT21hWHNDUnJ5aHk3YnFXek9DeA==" + }, + "method": "post", + "options": [], + "request_body": "", + "url": "https://example-app.auth0.com/oauth/token" + }, + "response": { + "binary": false, + "body": "{\"error\":\"something_wrong\",\"error_description\":\"Something went wrong\"}", + "headers": { + "Date": "Wed, 17 Jun 2020 23:39:36 GMT", + "Content-Type": "application/json", + "Content-Length": "69", + "Connection": "keep-alive", + "Server": "nginx", + "ot-tracer-spanid": "3085123a49690f22", + "ot-tracer-traceid": "2611213f15e78af3", + "ot-tracer-sampled": "true", + "ot-baggage-auth0-request-id": "d8916634ed436afe36744f6e", + "X-Auth0-RequestId": "1dd455670d641d81c4c8", + "Set-Cookie": "did=s%3Av0%3Acd966af0-b0e3-11ea-85ed-0d9365a06bc5.25colYvrl8qxIpnLc4gibEDFW5BvUQ6ryaj0zR1P7zc; Max-Age=31557600; Path=/; Expires=Fri, 18 Jun 2021 05:39:36 GMT; HttpOnly; Secure; SameSite=None", + "X-RateLimit-Limit": "30", + "X-RateLimit-Remaining": "29", + "X-RateLimit-Reset": "1592437177", + "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, no-transform" + }, + "status_code": 200, + "type": "ok" + } + } +] diff --git a/test/fixtures/vcr_cassettes/auth0-responses.json b/test/fixtures/vcr_cassettes/auth0-responses.json new file mode 100644 index 0000000..b42c121 --- /dev/null +++ b/test/fixtures/vcr_cassettes/auth0-responses.json @@ -0,0 +1,57 @@ +[ + { + "request": { + "body": "client_id=clientidKFpqmR7aO1iVUOQR1LMKbZvk&client_secret=clientsecrethzqAhvWpJhAehfkzupWelgut-hRh7ZYOBOmaXsCRryhy7bqWzOCx&code=code_abc&grant_type=authorization_code&redirect_uri=http%3A%2F%2Fwww.example.com%2Fauth%2Fauth0%2Fcallback", + "headers": { + "content-type": "application/x-www-form-urlencoded", + "accept": "application/json", + "authorization": "Basic Y2xpZW50aWRLRnBxbVI3YU8xaVZVT1FSMUxNS2Jadms6Y2xpZW50c2VjcmV0aHpxQWh2V3BKaEFlaGZrenVwV2VsZ3V0LWhSaDdaWU9CT21hWHNDUnJ5aHk3YnFXek9DeA==" + }, + "method": "post", + "options": [], + "request_body": "", + "url": "https://example-app.auth0.com/oauth/token" + }, + "response": { + "binary": false, + "body": "{\"access_token\":\"eyJz93alolk4laUWw\",\"refresh_token\":\"GEbRxBNkitedjnXbL\",\"id_token\":\"eyJ0XAipop4faeEoQ\",\"token_type\":\"Bearer\",\"expires_in\":86400}", + "headers": { + "Date": "Wed, 17 Jun 2020 23:39:36 GMT", + "Content-Type": "application/json", + "Content-Length": "144", + "Connection": "keep-alive", + "X-Auth0-RequestId": "1dd41235665d641d81c4c8", + }, + "status_code": 200, + "type": "ok" + } + }, + { + "request": { + "body": "", + "headers": { + "accept": "application/json", + "authorization": "Bearer eyJz93alolk4laUWw" + }, + "method": "get", + "options": [], + "request_body": "", + "url": "https://example-app.auth0.com/userinfo" + }, + "response": { + "binary": false, + "body": "{\"email\":\"testuser@example.com\",\"email_verified\":false,\"name\":\"testuser@example.com\",\"nickname\":\"testuser\",\"picture\":\"https://s.gravatar.com/avatar/7ec7606c46a14a7ef514d1f1f9038823?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Ftu.png\",\"sub\":\"auth0|lyy5vutbn9qfmihil7pvpo66\",\"updated_at\":\"2020-02-19T12:34:56.784Z\"}", + "headers": { + "Date": "Wed, 17 Jun 2020 23:39:36 GMT", + "Content-Type": "application/json", + "Content-Length": "144", + "Connection": "keep-alive", + "Server": "nginx", + "X-Auth0-RequestId": "1dd968479496d641d81c4c8", + "cache-control": "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, no-transform" + }, + "status_code": 200, + "type": "ok" + } + } +] diff --git a/test/strategy/auth0/oauth_test.exs b/test/strategy/auth0/oauth_test.exs new file mode 100644 index 0000000..8d17c26 --- /dev/null +++ b/test/strategy/auth0/oauth_test.exs @@ -0,0 +1,21 @@ +defmodule Ueberauth.Strategy.Auth0.OAuthTest do + use ExUnit.Case + + import Ueberauth.Strategy.Auth0.OAuth, only: [client: 0] + + @test_domain "example-app.auth0.com" + + setup do + {:ok, %{client: client()}} + end + + test "creates correct client", %{client: client} do + assert client.client_id == "clientidsomethingrandom" + assert client.client_secret == "clientsecret-somethingsecret" + assert client.redirect_uri == "" + assert client.strategy == Ueberauth.Strategy.Auth0.OAuth + assert client.authorize_url == "https://#{@test_domain}/authorize" + assert client.token_url == "https://#{@test_domain}/oauth/token" + assert client.site == "https://#{@test_domain}" + end +end diff --git a/test/strategy/auth0_test.exs b/test/strategy/auth0_test.exs new file mode 100644 index 0000000..d7fb04f --- /dev/null +++ b/test/strategy/auth0_test.exs @@ -0,0 +1,189 @@ +defmodule Ueberauth.Strategy.Auth0Test do + # Test resources: + use ExUnit.Case, async: true + use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney + + use Plug.Test + + # Custom data: + import Ueberauth.Strategy.Auth0, only: [info: 1] + alias Ueberauth.Auth.Info + + # Initializing utils: + doctest Ueberauth.Strategy.Auth0 + + @router SpecRouter.init([]) + @test_email "testuser@example.com" + + # Setups: + setup_all do + # Creating token: + token = %OAuth2.AccessToken{ + access_token: "eyJz93alolk4laUWw", + expires_at: 1_592_551_369, + other_params: %{"id_token" => "eyJ0XAipop4faeEoQ"}, + refresh_token: "GEbRxBNkitedjnXbL", + token_type: "Bearer" + } + + # Read the fixture with the user information: + {:ok, json} = + "test/fixtures/auth0.json" + |> Path.expand() + |> File.read() + + user_info = Jason.decode!(json) + + {:ok, response} = + "test/fixtures/auth0_response.html" + |> Path.expand() + |> File.read() + + response = String.replace(response, "\n", "") + + {:ok, + %{ + user_info: user_info, + token: token, + response: response + }} + end + + # Tests: + + test "request phase", fixtures do + conn = + :get + |> conn("/auth/auth0") + |> SpecRouter.call(@router) + + assert conn.resp_body == fixtures.response + end + + test "default callback phase" do + query = %{code: "code_abc"} |> URI.encode_query() + + use_cassette "auth0-responses" do + conn = + :get + |> conn("/auth/auth0/callback?#{query}") + |> SpecRouter.call(@router) + + assert conn.resp_body == "auth0 callback" + + auth = conn.assigns.ueberauth_auth + + assert auth.provider == :auth0 + assert auth.strategy == Ueberauth.Strategy.Auth0 + assert auth.uid == "auth0|lyy5vutbn9qfmihil7pvpo66" + end + end + + test "callback without code" do + # Empty query + query = %{} |> URI.encode_query() + + use_cassette "auth0-responses" do + conn = + :get + |> conn("/auth/auth0/callback?#{query}") + |> SpecRouter.call(@router) + + assert conn.resp_body == "auth0 callback" + + auth = conn.assigns.ueberauth_failure + + missing_code_error = %Ueberauth.Failure.Error{ + message: "No code received", + message_key: "missing_code" + } + + assert auth.provider == :auth0 + assert auth.strategy == Ueberauth.Strategy.Auth0 + assert auth.errors == [missing_code_error] + end + end + + test "callback with invalid code" do + # Empty query + query = %{code: "invalid_code"} |> URI.encode_query() + + use_cassette "auth0-invalid-code" do + assert_raise(OAuth2.Error, ~r/Server responded with status: 403.*/, fn -> + :get + |> conn("/auth/auth0/callback?#{query}") + |> SpecRouter.call(@router) + end) + end + end + + test "callback with no token in response" do + # Empty query + query = %{code: "some_code"} |> URI.encode_query() + + use_cassette "auth0-no-access-token" do + conn = + :get + |> conn("/auth/auth0/callback?#{query}") + |> SpecRouter.call(@router) + + assert conn.resp_body == "auth0 callback" + + auth = conn.assigns.ueberauth_failure + + missing_code_error = %Ueberauth.Failure.Error{ + message: "Something went wrong", + message_key: "something_wrong" + } + + assert auth.provider == :auth0 + assert auth.strategy == Ueberauth.Strategy.Auth0 + assert auth.errors == [missing_code_error] + end + end + + # This doesn't work yet, we'll add it when the feature exists + # test "callback phase with state" do + # query = %{code: "code_abc", state: "custom_state_value"} |> URI.encode_query + # + # use_cassette "auth0-responses" do + # conn = + # :get + # |> conn("/auth/auth0/callback?#{query}") + # |> SpecRouter.call(@router) + # + # assert conn.resp_body == "auth0 callback" + # + # auth = conn.assigns.ueberauth_auth + # + # assert auth.provider == :auth0 + # assert auth.strategy == Ueberauth.Strategy.Auth0 + # assert auth.uid == "auth0|lyy5vutbn9qfmihil7pvpo66" + # end + # end + + test "user information parsing", fixtures do + user_info = fixtures.user_info + token = fixtures.token + + conn = %Plug.Conn{ + private: %{ + auth0_user: user_info, + auth0_token: token + } + } + + assert info(conn) == %Info{ + email: @test_email, + name: @test_email, + first_name: nil, + last_name: nil, + nickname: "testuser", + location: nil, + description: nil, + image: + "https://s.gravatar.com/avatar/7ec7606c46a14a7ef514d1f1f9038823?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Ftu.png", + urls: %{} + } + end +end diff --git a/test/test_helper.exs b/test/test_helper.exs index 869559e..cace2a6 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1 +1,22 @@ +defmodule SpecRouter do + # Credit goes to: + # https://github.com/he9qi/ueberauth_weibo/blob/master/test/test_helper.exs + # and + # https://github.com/ueberauth/ueberauth_vk/blob/master/test/test_helper.exs + + require Ueberauth + use Plug.Router + + plug(:fetch_query_params) + + plug(Ueberauth, base_path: "/auth") + + plug(:match) + plug(:dispatch) + + get("/auth/auth0", do: send_resp(conn, 200, "auth0 request")) + + get("/auth/auth0/callback", do: send_resp(conn, 200, "auth0 callback")) +end + ExUnit.start() diff --git a/test/ueberauth_auth0_test.exs b/test/ueberauth_auth0_test.exs deleted file mode 100644 index 158d22d..0000000 --- a/test/ueberauth_auth0_test.exs +++ /dev/null @@ -1,8 +0,0 @@ -defmodule UeberauthAuth0Test do - use ExUnit.Case - doctest UeberauthAuth0 - - test "the truth" do - assert 1 + 1 == 2 - end -end