diff --git a/apps/authenticator/test/test_helper.exs b/apps/authenticator/test/test_helper.exs index a03bca7..559d899 100644 --- a/apps/authenticator/test/test_helper.exs +++ b/apps/authenticator/test/test_helper.exs @@ -1,4 +1,4 @@ -ExUnit.configure(formatters: [JUnitFormatter]) +ExUnit.configure(formatters: [JUnitFormatter, ExUnit.CLIFormatter]) ExUnit.start() Ecto.Adapters.SQL.Sandbox.mode(Authenticator.Repo, :manual) diff --git a/apps/authorizer/test/test_helper.exs b/apps/authorizer/test/test_helper.exs index 9f0e873..3140500 100644 --- a/apps/authorizer/test/test_helper.exs +++ b/apps/authorizer/test/test_helper.exs @@ -1,2 +1,2 @@ -ExUnit.configure(formatters: [JUnitFormatter]) +ExUnit.configure(formatters: [JUnitFormatter, ExUnit.CLIFormatter]) ExUnit.start() diff --git a/apps/resource_manager/test/test_helper.exs b/apps/resource_manager/test/test_helper.exs index 285fb60..c6fd043 100644 --- a/apps/resource_manager/test/test_helper.exs +++ b/apps/resource_manager/test/test_helper.exs @@ -1,4 +1,4 @@ -ExUnit.configure(formatters: [JUnitFormatter]) +ExUnit.configure(formatters: [JUnitFormatter, ExUnit.CLIFormatter]) ExUnit.start() Ecto.Adapters.SQL.Sandbox.mode(ResourceManager.Repo, :manual) diff --git a/apps/rest_api/lib/controllers/admin/user.ex b/apps/rest_api/lib/controllers/admin/user.ex index 00c8628..d7472d0 100644 --- a/apps/rest_api/lib/controllers/admin/user.ex +++ b/apps/rest_api/lib/controllers/admin/user.ex @@ -27,4 +27,20 @@ defmodule RestAPI.Controller.Admin.User do error end end + + def show(conn, %{"id" => username} = params) do + params + |> Map.put(:username, username) + |> ResourceManager.get_identity() + |> case do + {:ok, identity} when is_map(identity) -> + conn + |> put_status(:created) + |> put_view(User) + |> render("show.json", response: identity) + + {:error, error_reason} -> + error_reason + end + end end diff --git a/apps/rest_api/lib/ports/resource_manager.ex b/apps/rest_api/lib/ports/resource_manager.ex index 2ae1538..89c1901 100644 --- a/apps/rest_api/lib/ports/resource_manager.ex +++ b/apps/rest_api/lib/ports/resource_manager.ex @@ -7,16 +7,25 @@ defmodule RestAPI.Ports.ResourceManager do @type possible_create_identity_response :: {:ok, struct()} | {:error, Ecto.Changeset.t() | :invalid_params} + @type possible_get_identity_responses :: + {:ok, struct()} | {:error, :not_found | :invalid_params} + @doc "Delegates to ResourceManager.create_identity/1" @callback create_identity(input :: map()) :: possible_create_identity_response() @doc "Delegates to ResourceManager.password_allowed?/1" @callback password_allowed?(password :: String.t()) :: boolean() + @callback get_identity(input :: map()) :: possible_get_identity_responses() + @doc "Create a new identity with it's credentials" @spec create_identity(input :: map()) :: possible_create_identity_response() def create_identity(input), do: implementation().create_identity(input) + @doc "Returns an user or application identity seaching by the given input" + @spec get_identity(input :: map()) :: possible_get_identity_responses() + def get_identity(input), do: implementation().get_identity(input) + @doc "Checks if the given password is strong enough to be used" @spec password_allowed?(password :: String.t()) :: boolean() def password_allowed?(password), do: implementation().password_allowed?(password) diff --git a/apps/rest_api/lib/views/admin/user.ex b/apps/rest_api/lib/views/admin/user.ex index d2696ba..2e16274 100644 --- a/apps/rest_api/lib/views/admin/user.ex +++ b/apps/rest_api/lib/views/admin/user.ex @@ -13,4 +13,15 @@ defmodule RestAPI.Views.Admin.User do update_at: response.updated_at } end + + def render("show.json", %{response: response}) do + %{ + id: response.id, + username: response.username, + status: response.status, + is_admin: response.is_admin, + inserted_at: response.inserted_at, + updated_at: response.updated_at + } + end end diff --git a/apps/rest_api/test/controllers/admin/user_test.exs b/apps/rest_api/test/controllers/admin/user_test.exs index 8d765b2..3a4ff00 100644 --- a/apps/rest_api/test/controllers/admin/user_test.exs +++ b/apps/rest_api/test/controllers/admin/user_test.exs @@ -5,6 +5,7 @@ defmodule RestAPI.Controllers.Admin.User do alias RestAPI.Ports.{AuthenticatorMock, AuthorizerMock, ResourceManagerMock} @create_endpoint "/admin/v1/users" + @show_endpoint "/admin/v1/users/" describe "POST #{@create_endpoint}" do setup do @@ -163,6 +164,97 @@ defmodule RestAPI.Controllers.Admin.User do end end + describe "GET #{@show_endpoint}" do + setup do + access_token = "my-access-token" + claims = default_claims() + + {:ok, access_token: access_token, claims: claims, user: insert!(:user)} + end + + test "should render user identity", %{ + conn: conn, + access_token: access_token, + claims: claims, + user: user + } do + username = user.username + + expect(AuthenticatorMock, :validate_access_token, fn token -> + assert access_token == token + {:ok, claims} + end) + + expect(AuthenticatorMock, :get_session, fn %{"jti" => jti} -> + assert claims["jti"] == jti + {:ok, success_session(claims)} + end) + + expect(AuthorizerMock, :authorize_admin, fn %Plug.Conn{} -> :ok end) + + expect(ResourceManagerMock, :get_identity, fn input -> + assert is_map(input) + + {:ok, + %{ + id: user.id, + inserted_at: NaiveDateTime.utc_now(), + is_admin: user.is_admin, + status: user.status, + updated_at: NaiveDateTime.utc_now(), + username: username + }} + end) + + assert %{ + "id" => _id, + "inserted_at" => _inserted_at, + "updated_at" => _updated_at, + "is_admin" => false, + "status" => "active", + "username" => ^username + } = + conn + |> put_req_header("authorization", "Bearer #{access_token}") + |> get(@show_endpoint <> "username") + |> json_response(201) + end + + test "if id is not an id, should return error", %{ + conn: conn, + access_token: access_token, + claims: claims + } do + expect(AuthenticatorMock, :validate_access_token, fn token -> + assert access_token == token + {:ok, claims} + end) + + expect(AuthenticatorMock, :get_session, fn %{"jti" => jti} -> + assert claims["jti"] == jti + {:ok, success_session(claims)} + end) + + expect(AuthorizerMock, :authorize_admin, fn %Plug.Conn{} -> :ok end) + + expect(ResourceManagerMock, :get_identity, fn input -> + assert is_map(input) + + {:error, {:error, :invalid_params}} + end) + + assert %{ + "detail" => "The given parameters are invalid", + "error" => "bad_request", + "status" => 400 + } = + conn + |> put_req_header("authorization", "Bearer #{access_token}") + |> get(@show_endpoint <> "1") + |> json_response(400) + end + end + defp default_claims do %{ "jti" => "03eds74a-c291-4b5f", diff --git a/apps/rest_api/test/support/conn_case.ex b/apps/rest_api/test/support/conn_case.ex index 5f30615..cdfccba 100644 --- a/apps/rest_api/test/support/conn_case.ex +++ b/apps/rest_api/test/support/conn_case.ex @@ -16,13 +16,14 @@ defmodule RestAPI.ConnCase do """ use ExUnit.CaseTemplate + alias Ecto.Adapters.SQL.Sandbox using do quote do # Import conveniences for testing with connections import Plug.Conn import Phoenix.ConnTest - import RestAPI.ConnCase + import RestAPI.{ConnCase, Factory} import Mox alias RestAPI.Router.Helpers, as: Routes @@ -32,7 +33,13 @@ defmodule RestAPI.ConnCase do end end - setup _tags do + setup tags do + :ok = Sandbox.checkout(ResourceManager.Repo) + + unless tags[:async] do + Sandbox.mode(ResourceManager.Repo, {:shared, self()}) + end + {:ok, conn: Phoenix.ConnTest.build_conn()} end end diff --git a/apps/rest_api/test/support/factory.ex b/apps/rest_api/test/support/factory.ex new file mode 100644 index 0000000..8f787db --- /dev/null +++ b/apps/rest_api/test/support/factory.ex @@ -0,0 +1,32 @@ +defmodule RestAPI.Factory do + @moduledoc false + + alias ResourceManager.Identities.Schemas.User + alias ResourceManager.Repo + + @doc "Builds a default struct from the requested model" + @spec build(model :: atom()) :: struct() + def build(:user) do + %User{ + username: "my-test-username#{System.unique_integer()}", + status: "active", + is_admin: false + } + end + + @doc "Returns the a model struct with the given attributes" + @spec build(factory_name :: atom(), attributes :: Keyword.t()) :: struct() + def build(factory_name, attributes) when is_atom(factory_name) and is_list(attributes) do + factory_name + |> build() + |> struct!(attributes) + end + + @doc "Inserts a model with the given attributes on database" + @spec insert!(factory_name :: atom(), attributes :: Keyword.t()) :: struct() + def insert!(factory_name, attributes \\ []) when is_atom(factory_name) do + factory_name + |> build(attributes) + |> Repo.insert!() + end +end diff --git a/apps/rest_api/test/test_helper.exs b/apps/rest_api/test/test_helper.exs index 9f0e873..3140500 100644 --- a/apps/rest_api/test/test_helper.exs +++ b/apps/rest_api/test/test_helper.exs @@ -1,2 +1,2 @@ -ExUnit.configure(formatters: [JUnitFormatter]) +ExUnit.configure(formatters: [JUnitFormatter, ExUnit.CLIFormatter]) ExUnit.start()