Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions lib/code_corps_web/controllers/user_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,36 +36,35 @@ defmodule CodeCorpsWeb.UserController do
{:ok, :authorized} <- current_user |> Policy.authorize(:update, user),
{:ok, user, _, _} <- user |> UserService.update(params)
do
conn |> render("show.json-api", data: user)
conn |> render("show.json-api", data: user)
end
end

@doc """
Differs from other resources by path: `/oauth/github`
"""
def github_oauth(conn, %{"code" => code, "state" => state}) do
@spec github_oauth(Conn.t, map) :: Conn.t
def github_oauth(%Conn{} = conn, %{"code" => code, "state" => state}) do
current_user = Guardian.Plug.current_resource(conn)
with {:ok, user} <- GitHub.User.connect(current_user, code, state)
do
conn |> render("show.json-api", data: user)
else
{:error, %Ecto.Changeset{} = changeset} ->
conn
|> put_status(:unprocessable_entity)
|> render(CodeCorpsWeb.ChangesetView, "error.json-api", changeset: changeset)
{:error, _error} ->
conn
|> put_status(:internal_server_error)
|> render(CodeCorpsWeb.ErrorView, "500.json-api")
end
end

def email_available(conn, %{"email" => email}) do
@spec email_available(Conn.t, map) :: Conn.t
def email_available(%Conn{} = conn, %{"email" => email}) do
hash = User.check_email_availability(email)
conn |> json(hash)
end

def username_available(conn, %{"username" => username}) do
@spec username_available(Conn.t, map) :: Conn.t
def username_available(%Conn{} = conn, %{"username" => username}) do
hash = User.check_username_availability(username)
conn |> json(hash)
end
Expand Down
2 changes: 1 addition & 1 deletion test/lib/code_corps/github/user_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ defmodule CodeCorps.GitHub.UserTest do
error = GitHub.APIError.new({404, %{"message" => "{\"error\":\"Not Found\"}"}})

with_mock_api(NotFoundRequest) do
assert {:error, error } == GitHub.User.connect(user, "foo_code", "foo_state")
assert {:error, error} == GitHub.User.connect(user, "foo_code", "foo_state")
end
end
end
Expand Down
32 changes: 19 additions & 13 deletions test/lib/code_corps_web/controllers/user_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ defmodule CodeCorpsWeb.UserControllerTest do

use CodeCorpsWeb.ApiCase, resource_name: :user

import CodeCorps.GitHub.TestHelpers

alias CodeCorps.{User, Repo}

@valid_attrs %{
Expand Down Expand Up @@ -243,26 +245,30 @@ defmodule CodeCorpsWeb.UserControllerTest do
end
end


describe "github_oauth" do
test "return the user when current user connects successfully", %{conn: conn} do
user = insert(:user)

json = %{"code" => "valid_code", "state" => "valid_state"}

path = user_path(conn, :github_oauth, json)
@attrs %{"code" => "foo", "state" => "bar"}
@tag :authenticated
test "return the user when current user connects successfully", %{conn: conn, current_user: current_user} do
path = user_path(conn, :github_oauth)

json = conn |> authenticate(user) |> post(path) |> json_response(200)
json = conn |> post(path, @attrs) |> json_response(200)

assert json["data"]["id"] |> String.to_integer == user.id
assert json["data"]["id"] |> String.to_integer == current_user.id
assert json["data"]["attributes"]["github-id"]
end

test "return unauthenticated error code when no current user", %{conn: conn} do
json = %{"code" => "client generated code", "state" => "state"}
test "requires authentication", %{conn: conn} do
path = user_path(conn, :github_oauth)
assert conn |> post(path, @attrs) |> json_response(401)
end

path = user_path(conn, :github_oauth, json)
@tag :authenticated
test "renders 500 if there's a GitHub API error", %{conn: conn} do
path = user_path(conn, :github_oauth)

conn |> post(path) |> json_response(401)
with_mock_api(CodeCorps.GitHub.FailureAPI) do
assert conn |> post(path, @attrs) |> json_response(500)
end
end
end

Expand Down
37 changes: 37 additions & 0 deletions test/support/github/failure_api.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
defmodule CodeCorps.GitHub.FailureAPI do
@moduledoc ~S"""
A basic GitHub API mock which returns a 401 forbidden for all requests.

Should be good enough for any tests that simply assert a piece of code is able
to recover from a generic request error.

For any tests that cover handling of specific errors, a non-default API should
be defined inline.

Since our GitHub requests are often forced to start with an installation
access token request, that one is set to succeed here as well.
"""
import CodeCorps.GitHub.TestHelpers

alias CodeCorps.GitHub.SuccessAPI

def request(method, url, headers, body, options) do
case {method, url} |> for_access_token?() do
true -> SuccessAPI.request(method, url, headers, body, options)
false ->
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if there's a way to metaprogram this and use an @impl or something?

send(self(), {method, url, headers, body, options})
body = load_endpoint_fixture("forbidden")
error = CodeCorps.GitHub.APIError.new({401, body})
{:error, error}
end
end

defp for_access_token?({:post, url}), do: url |> access_token_url?()
defp for_access_token?({_method, _url}), do: false

defp access_token_url?("https://api.github.com/" <> path), do: path |> String.split("/") |> access_token_parts?()
defp access_token_url?(_), do: false

defp access_token_parts?(["installations", _, "access_tokens"]), do: true
defp access_token_parts?(_), do: false
end