Compare commits

...

3 Commits

Author SHA1 Message Date
Nat ebb83274f5
Implement controller tests for denizens 2023-07-27 02:00:38 -07:00
Nat e983ef7ce1
Implement core denizen interface 2023-06-15 09:25:59 -07:00
Nat cdbb6646eb
Scaffold denizen controller 2023-06-14 10:46:23 -07:00
6 changed files with 198 additions and 1 deletions

View File

@ -1,6 +1,10 @@
defmodule Hostas.Denizen do
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query, only: [from: 2]
alias Hostas.Denizen
alias Hostas.Repo
schema "denizens" do
field :handle, :string
@ -27,6 +31,46 @@ defmodule Hostas.Denizen do
%{password_hash: hash} = Bcrypt.add_hash(password)
change(changeset, password: hash)
end
defp hash_password(changeset), do: changeset
defp query_by_handle(handle) do
from d in Denizen, where: d.handle == ^handle
end
@doc """
Creates a denizen. Returns the result of the Ecto.Repo insertion
"""
def create(denizen) do
Repo.insert(changeset(%Denizen{}, denizen))
end
@doc """
Returns a denizen
"""
def get(handle) do
case query_by_handle(handle) |> Repo.one do
nil -> {:error, :not_found}
struct -> {:ok, struct}
end
end
@doc """
deactivates a denizen's account. This should mark their account as deactivated
and replace all their content with tombstones
"""
def deactivate(handle) do
handle
|> query_by_handle
|> Repo.delete_all
:ok
end
@doc """
Modifies all the denizen's fields except `:id`, replacing them with the
corresponding values in the map
"""
def update(%{"handle" => handle} = update_map) do
Repo.update(changeset(get(handle), update_map))
end
end

View File

@ -46,6 +46,8 @@ defmodule HostasWeb do
import HostasWeb.Gettext
unquote(verified_routes())
import HostasWeb.ControllerHelpers
end
end

View File

@ -0,0 +1,11 @@
defmodule HostasWeb.ControllerHelpers do
import Plug.Conn
@doc """
Mark an endpoint as unimplemented
"""
def unimplemented(conn) do
conn
|> send_resp(501, "")
end
end

View File

@ -0,0 +1,24 @@
defmodule HostasWeb.DenizenController do
# import Ecto.Query, only: [from: 2]
use HostasWeb, :controller
# alias Hostas.Repo
# alias Hostas.Denizen
# alias Hostas.Token
def register(conn, _params) do
unimplemented(conn)
end
def deactivate(conn, _params) do
unimplemented(conn)
end
def show(conn, _params) do
unimplemented(conn)
end
def update(conn, _params) do
unimplemented(conn)
end
end

View File

@ -35,6 +35,12 @@ defmodule HostasWeb.Router do
get "/auth/token", Auth.TokenController, :verify
delete "/auth/token/:id", Auth.TokenController, :revoke
get "/auth/token/:id/renew", Auth.TokenController, :renew
post "/denizen", DenizenController, :register
delete "/denizen/:id", DenizenController, :remove
get "/denizen/:id", DenizenController, :show
patch "/denizen/:id", DenizenController, :update
end
# Enable LiveDashboard and Swoosh mailbox preview in development

View File

@ -0,0 +1,110 @@
defmodule HostasWeb.Auth.DenizenControllerTest do
use HostasWeb.ConnCase
# For testing with Ecto
import Ecto.Query, only: [from: 2]
alias Hostas.Repo
alias Hostas.Denizen
alias Hostas.Token
@denizen %{
"handle" => "test",
"name" => "Test Denizen",
"password" => "password",
}
setup_all %{conn: conn} do
denizen1 = %Denizen{}
|> Denizen.changeset(%{handle: "denizen1", name: "Test Denizen 1", password: "password"})
|> Repo.insert!
denizen1 = Denizen.create(%{handle: "denizen1", name: "Test Denizen 1", password: "password"})
denizen2 = Denizen.create(%{handle: "denizen2", name: "Test Denizen 2", password: "password"})
{:ok, token} = Token.new(denizen1.id)
# Facilitate requests to protected routes
conn = put_req_header(conn, "authorization", "Bearer #{struct.token}")
%{denizen1: denizen1, denizen2: denizen2, token: token, conn: conn}
end
describe "register denizen" do
test "succeeds", %{conn: conn} do
conn = post(conn, ~p"/hostapi/denizen/", @denizen)
assert json_response(conn, 201)["handle"] == @denizen["handle"]
assert json_response(conn, 201)["name"] == @denizen["name"]
assert json_response(conn, 201)["id"] == @denizen["id"]
end
test "fails because duplicate handle", %{conn: conn, denizen1: pre_existing_denizen} do
pre_existing_handle = pre_existing_denizen["handle"]
conn = post(
conn,
~p"/hostapi/denizen/",
Map.put(@denizen, "handle", pre_existing_handle)
)
assert json_response(conn, 422)["error"] ==
"Denizen with handle #{pre_existing_handle} already exists"
end
end
describe "show denizen" do
test "succeeds", %{conn: conn, denizen1: denizen} do
%{handle: handle} = denizen
conn = get(conn, ~p"/hostapi/denizen/#{handle}")
assert json_response(conn, 200) == Map.delete(denizen, :password)
end
test "fails because handle is unknown", %{conn: conn} do
conn = get(conn, ~p"/hostapi/denizen/unknown_denizen")
assert json_response(conn, 404)["error"] == "No denizen corresponding to given handle"
end
end
describe "deactivate denizen" do
test "returns 201 when done by same denizen", %{conn: conn, denizen1: denizen} do
%{handle: handle} = denizen
conn = delete(conn, ~p"/hostapi/denizen/#{handle}")
assert json_response(conn, 201)
end
test "succeeds", %{conn: conn, denizen1: denizen} do
%{handle: handle} = denizen
conn = delete(conn, ~p"/hostapi/denizen/#{handle}")
assert json_response(conn, 201)
end
test "fails because denizens can't deactivate one another", %{conn: conn, denizen2: denizen2} do
conn = delete(conn, ~p"/hostapi/denizen/#{denizen2.handle}")
assert json_response(conn, 403)["error"] == "Regular denizens cannot deactivate other denizens"
end
test "fails because referenced denizen doesn't exist", %{conn: conn, denizen1: denizen} do
conn = delete(conn, ~p"/hostapi/denizen/unknown_denizen")
assert json_response(conn, 403)["error"] == "Regular denizens cannot deactivate other denizens"
end
end
describe "update denizen" do
test "succeeds", %{conn: conn, denizen1: denizen} do
update = Map.put(denizen, :name, "Updated Name")
conn = patch(conn, ~p"/hostapi/denizen/#{denizen.handle}", update)
assert json_response(conn, 200) == update
end
test "fails because denizen can't modify someone else's account", %{conn: conn, denizen2: denizen2} do
conn = patch(conn, ~p"/hostapi/denizen/#{denizen.handle}", %{})
assert json_response(conn, 403)["error"] == "Regular denizens cannot update other denizens"
end
test "fails because denizen doesn't exist", %{conn: conn} do
conn = patch(conn, ~p"/hostapi/denizen/unknown_denizen", %{})
assert json_response(conn, 403)["error"] == "Regular denizens cannot update other denizens"
end
end
end