Sessions and Users controller testing

This commit is contained in:
Jamil Bou Kheir
2020-06-04 08:40:31 -07:00
parent 5e7279aac1
commit a57ec388ed
18 changed files with 223 additions and 191 deletions

View File

@@ -8,50 +8,10 @@ defmodule FgHttp.Devices do
alias FgHttp.Devices.Device
@doc """
Returns the list of devices.
## Examples
iex> list_devices()
[%Device{}, ...]
"""
def list_devices, do: Repo.all(Device)
def list_devices(user_id) do
Repo.all(from d in Device, where: d.user_id == ^user_id)
end
@doc """
Returns a new device with an initialized configuration.
## Examples
iex> new_device()
%Device{
"conf" => "new_conf"
}
"""
def new_device(attrs \\ %{}) do
device = %Device{}
Map.merge(device, attrs)
end
@doc """
Gets a single device.
Raises `Ecto.NoResultsError` if the Device does not exist.
## Examples
iex> get_device!(123)
%Device{}
iex> get_device!(456)
** (Ecto.NoResultsError)
"""
def get_device!(id), do: Repo.get!(Device, id)
def get_device!(id, with_rules: true) do
@@ -62,67 +22,22 @@ defmodule FgHttp.Devices do
)
end
@doc """
Creates a device.
## Examples
iex> create_device(%{field: value})
{:ok, %Device{}}
iex> create_device(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_device(attrs \\ %{}) do
%Device{}
|> Device.changeset(attrs)
|> Repo.insert()
end
@doc """
Updates a device.
## Examples
iex> update_device(device, %{field: new_value})
{:ok, %Device{}}
iex> update_device(device, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def update_device(%Device{} = device, attrs) do
device
|> Device.changeset(attrs)
|> Repo.update()
end
@doc """
Deletes a device.
## Examples
iex> delete_device(device)
{:ok, %Device{}}
iex> delete_device(device)
{:error, %Ecto.Changeset{}}
"""
def delete_device(%Device{} = device) do
Repo.delete(device)
end
@doc """
Returns an `%Ecto.Changeset{}` for tracking device changes.
## Examples
iex> change_device(device)
%Ecto.Changeset{source: %Device{}}
"""
def change_device(%Device{} = device) do
Device.changeset(device, %{})
end

View File

@@ -20,7 +20,6 @@ defmodule FgHttp.Devices.Device do
timestamps(type: :utc_datetime_usec)
end
@doc false
def changeset(device, attrs) do
device
|> cast(attrs, [:last_ip, :ifname, :user_id, :name, :public_key])

View File

@@ -8,15 +8,6 @@ defmodule FgHttp.Rules do
alias FgHttp.Rules.Rule
@doc """
Returns the list of rules.
## Examples
iex> list_rules()
[%Rule{}, ...]
"""
def list_rules(device_id) do
Repo.all(from r in Rule, where: r.device_id == ^device_id)
end
@@ -25,83 +16,24 @@ defmodule FgHttp.Rules do
Repo.all(Rule)
end
@doc """
Gets a single rule.
Raises `Ecto.NoResultsError` if the Rule does not exist.
## Examples
iex> get_rule!(123)
%Rule{}
iex> get_rule!(456)
** (Ecto.NoResultsError)
"""
def get_rule!(id), do: Repo.get!(Rule, id)
@doc """
Creates a rule.
## Examples
iex> create_rule(%{field: value})
{:ok, %Rule{}}
iex> create_rule(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_rule(attrs \\ %{}) do
%Rule{}
|> Rule.changeset(attrs)
|> Repo.insert()
end
@doc """
Updates a rule.
## Examples
iex> update_rule(rule, %{field: new_value})
{:ok, %Rule{}}
iex> update_rule(rule, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def update_rule(%Rule{} = rule, attrs) do
rule
|> Rule.changeset(attrs)
|> Repo.update()
end
@doc """
Deletes a rule.
## Examples
iex> delete_rule(rule)
{:ok, %Rule{}}
iex> delete_rule(rule)
{:error, %Ecto.Changeset{}}
"""
def delete_rule(%Rule{} = rule) do
Repo.delete(rule)
end
@doc """
Returns an `%Ecto.Changeset{}` for tracking rule changes.
## Examples
iex> change_rule(rule)
%Ecto.Changeset{source: %Rule{}}
"""
def change_rule(%Rule{} = rule) do
Rule.changeset(rule, %{})
end

View File

@@ -21,7 +21,6 @@ defmodule FgHttp.Rules.Rule do
timestamps(type: :utc_datetime_usec)
end
@doc false
def changeset(rule, attrs) do
rule
|> cast(attrs, [:device_id, :priority, :action, :destination, :port, :protocol, :enabled])

View File

@@ -24,6 +24,8 @@ defmodule FgHttp.Sessions do
"""
def get_session!(email: email), do: Repo.get_by!(Session, email: email)
def get_session!(id), do: Repo.get!(Session, id)
def get_session(email: email), do: Repo.get_by(Session, email: email)
def get_session(id), do: Repo.get(Session, id)
@doc """
Creates a session.

View File

@@ -20,19 +20,16 @@ defmodule FgHttp.Users.PasswordReset do
field :email, :string
end
@doc false
def changeset do
%__MODULE__{}
|> cast(%{}, [:password, :password_confirmation, :reset_token])
end
@doc false
def changeset(%__MODULE__{} = password_reset, attrs \\ %{}) do
password_reset
|> cast(attrs, [:password, :password_confirmation, :reset_token])
end
@doc false
def create_changeset(%__MODULE__{} = password_reset, attrs) do
password_reset
|> cast(attrs, [:email, :reset_sent_at, :reset_token])
@@ -44,7 +41,6 @@ defmodule FgHttp.Users.PasswordReset do
|> validate_required([:reset_sent_at])
end
@doc false
def update_changeset(%__MODULE__{} = password_reset, attrs) do
password_reset
|> cast(attrs, [

View File

@@ -16,12 +16,16 @@ defmodule FgHttp.Users.Session do
def create_changeset(session, attrs \\ %{}) do
session
|> cast(attrs, [:email, :password])
|> validate_required([:email, :password])
|> cast(attrs, [:email, :password, :last_signed_in_at])
|> log_it()
|> authenticate_user()
|> set_last_signed_in_at()
end
defp log_it(changeset) do
changeset
end
defp set_last_signed_in_at(%Ecto.Changeset{valid?: true} = changeset) do
last_signed_in_at = DateTime.utc_now()
change(changeset, last_signed_in_at: last_signed_in_at)
@@ -32,22 +36,24 @@ defmodule FgHttp.Users.Session do
defp authenticate_user(
%Ecto.Changeset{
valid?: true,
changes: %{email: email, password: password}
changes: %{
password: password
}
} = changeset
) do
user = Users.get_user!(email: email)
session = changeset.data
user = Users.get_user!(email: session.email)
case User.authenticate_user(user, password) do
{:ok, _} ->
# Remove the user's password so it doesn't accidentally end up somewhere
changeset
|> delete_change(:password)
|> change(%{id: user.id})
{:error, error_msg} ->
raise("There was an issue with your password: #{error_msg}")
add_error(changeset, :password, "invalid: #{error_msg}")
end
end
defp authenticate_user(changeset), do: delete_change(changeset, :password)
defp authenticate_user(changeset) do
changeset
end
end

View File

@@ -25,7 +25,6 @@ defmodule FgHttp.Users.User do
timestamps(type: :utc_datetime_usec)
end
@doc false
def create_changeset(user, attrs \\ %{}) do
user
|> cast(attrs, [:email, :password_hash, :password, :password_confirmation])

View File

@@ -28,8 +28,8 @@ defmodule FgHttpWeb.DeviceController do
all_params = Map.merge(device_params, our_params)
case Devices.create_device(all_params) do
{:ok, device} ->
redirect(conn, to: Routes.device_path(conn, :show, device))
{:ok, _device} ->
redirect(conn, to: Routes.device_path(conn, :index))
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "new.html", changeset: changeset)

View File

@@ -28,9 +28,9 @@ defmodule FgHttpWeb.RuleController do
{:ok, rule} ->
conn
|> put_flash(:info, "Rule created successfully.")
|> redirect(to: Routes.rule_path(conn, :show, rule))
|> redirect(to: Routes.device_rule_path(conn, :index, rule.device_id))
{:error, %Ecto.Changeset{} = changeset} ->
{:error, changeset} ->
device = Devices.get_device!(device_id)
render(conn, "new.html", device: device, changeset: changeset)
end
@@ -55,19 +55,20 @@ defmodule FgHttpWeb.RuleController do
{:ok, rule} ->
conn
|> put_flash(:info, "Rule updated successfully.")
|> redirect(to: Routes.rule_path(conn, :show, rule))
|> redirect(to: Routes.device_rule_path(conn, :index, rule.device_id))
{:error, %Ecto.Changeset{} = changeset} ->
{:error, changeset} ->
render(conn, "edit.html", rule: rule, changeset: changeset)
end
end
def delete(conn, %{"id" => id}) do
rule = Rules.get_rule!(id)
device_id = rule.device_id
{:ok, _rule} = Rules.delete_rule(rule)
conn
|> put_flash(:info, "Rule deleted successfully.")
|> redirect(to: Routes.rule_path(conn, :index, rule.device))
|> redirect(to: Routes.device_rule_path(conn, :index, device_id))
end
end

View File

@@ -16,7 +16,7 @@ defmodule FgHttpWeb.SessionController do
# POST /sessions
def create(conn, %{"session" => %{"email" => email} = session_params}) do
case Sessions.get_session!(email: email) do
case Sessions.get_session(email: email) do
%Session{} = session ->
case Sessions.create_session(session, session_params) do
{:ok, session} ->

View File

@@ -10,16 +10,13 @@ defmodule FgHttpWeb.Plugs.SessionLoader do
def init(default), do: default
# Don't load an already loaded session
def call(%Plug.Conn{assigns: %{session: %Session{}}} = conn, _default), do: conn
def call(conn, _default) do
case get_session(conn, :user_id) do
nil ->
unauthed(conn)
user_id ->
case Sessions.get_session!(user_id) do
case Sessions.get_session(user_id) do
%Session{} = session ->
conn
|> assign(:session, session)
@@ -32,6 +29,7 @@ defmodule FgHttpWeb.Plugs.SessionLoader do
defp unauthed(conn) do
conn
|> clear_session()
|> put_flash(:error, "Please sign in to access that page.")
|> redirect(to: Routes.session_path(conn, :new))
|> halt()

View File

@@ -37,7 +37,8 @@ defmodule FgHttpWeb.Router do
resources "/rules", RuleController, only: [:show, :update, :delete, :edit]
resources "/sessions", SessionController, only: [:new, :create, :delete]
resources "/sessions", SessionController, only: [:new, :create]
resources "/session", SessionController, singleton: true, only: [:delete]
get "/", SessionController, :new
end

View File

@@ -23,8 +23,8 @@ defmodule FgHttpWeb.DeviceControllerTest do
describe "create device" do
test "redirects when data is valid", %{authed_conn: conn} do
conn = post(conn, Routes.device_path(conn, :create), device: @create_attrs)
assert html_response(conn, 302) =~ "redirected"
test_conn = post(conn, Routes.device_path(conn, :create), device: @create_attrs)
assert redirected_to(test_conn) == Routes.device_path(test_conn, :index)
end
test "renders errors when data is invalid", %{authed_conn: conn} do

View File

@@ -1,21 +1,115 @@
defmodule FgHttpWeb.RuleControllerTest do
use FgHttpWeb.ConnCase, async: true
alias FgHttp.Fixtures
@valid_create_attrs %{
destination: "1.1.1.1",
port: "53",
protocol: "udp"
}
@invalid_create_attrs %{
destination: "problem"
}
@valid_update_attrs @valid_create_attrs
@invalid_update_attrs @invalid_create_attrs
describe "index" do
setup [:create_device]
test "list all rules", %{authed_conn: conn, device: device} do
test_conn = get(conn, Routes.device_rule_path(conn, :index, device))
assert html_response(test_conn, 200) =~ "Listing Rules"
end
end
describe "new rule" do
describe "new" do
setup [:create_device]
test "renders form", %{authed_conn: conn, device: device} do
test_conn = get(conn, Routes.device_rule_path(conn, :new, device))
assert html_response(test_conn, 200) =~ "New Rule"
end
end
describe "create rule" do
describe "create" do
setup [:create_device]
test "redirects when data is valid", %{authed_conn: conn, device: device} do
test_conn =
post(conn, Routes.device_rule_path(conn, :create, device), rule: @valid_create_attrs)
assert redirected_to(test_conn) == Routes.device_rule_path(test_conn, :index, device)
end
test "renders edit when data is invalid", %{authed_conn: conn, device: device} do
test_conn =
post(conn, Routes.device_rule_path(conn, :create, device), rule: @invalid_create_attrs)
assert html_response(test_conn, 200) =~ "New Rule"
end
end
describe "edit rule" do
describe "edit" do
setup [:create_rule]
test "renders form", %{authed_conn: conn, rule: rule} do
test_conn = get(conn, Routes.rule_path(conn, :edit, rule))
assert html_response(test_conn, 200) =~ "Edit Rule"
end
end
describe "update rule" do
describe "show" do
setup [:create_rule]
test "renders the rule", %{authed_conn: conn, rule: rule} do
test_conn = get(conn, Routes.rule_path(conn, :show, rule))
assert html_response(test_conn, 200) =~ "Show Rule"
end
end
describe "delete rule" do
describe "update" do
setup [:create_rule]
test "redirects to index with valid attrs", %{authed_conn: conn, rule: rule} do
test_conn = put(conn, Routes.rule_path(conn, :update, rule), rule: @valid_update_attrs)
assert redirected_to(test_conn) ==
Routes.device_rule_path(test_conn, :index, rule.device_id)
end
test "renders edit form with invalid attrs", %{authed_conn: conn, rule: rule} do
test_conn = put(conn, Routes.rule_path(conn, :update, rule), rule: @invalid_update_attrs)
assert html_response(test_conn, 200) =~ "Edit Rule"
end
end
describe "delete" do
setup [:create_rule]
test "deletes chosen rule", %{authed_conn: conn, rule: rule} do
test_conn = delete(conn, Routes.rule_path(conn, :delete, rule))
assert redirected_to(test_conn) == Routes.device_rule_path(conn, :index, rule.device_id)
assert_error_sent 404, fn ->
get(conn, Routes.rule_path(conn, :show, rule))
end
end
end
defp create_device(_) do
device = Fixtures.device()
{:ok, device: device}
end
defp create_rule(_) do
rule = Fixtures.rule()
{:ok, rule: rule}
end
end

View File

@@ -0,0 +1,80 @@
defmodule FgHttpWeb.SessionControllerTest do
use FgHttpWeb.ConnCase, async: true
alias FgHttp.{Fixtures, Repo, Users.User}
@valid_attrs %{email: "test", password: "test"}
@invalid_attrs %{email: "test", password: "wrong"}
describe "new" do
test "renders sign in form", %{unauthed_conn: conn} do
test_conn = get(conn, Routes.session_path(conn, :new))
assert html_response(test_conn, 200) =~ "Sign In"
end
end
describe "create when user exists" do
setup [:create_user]
test "creates session when credentials are valid", %{unauthed_conn: conn, user: user} do
test_conn = post(conn, Routes.session_path(conn, :create), session: @valid_attrs)
assert redirected_to(test_conn) == Routes.device_path(test_conn, :index)
assert get_flash(test_conn, :info) == "Session created successfully"
assert get_session(test_conn, :user_id) == user.id
end
test "displays error if credentials are invalid", %{unauthed_conn: conn} do
test_conn = post(conn, Routes.session_path(conn, :create), session: @invalid_attrs)
assert html_response(test_conn, 200) =~ "Sign In"
assert get_flash(test_conn, :error) == "Error creating session."
end
end
describe "create when user doesn't exist" do
setup [:clear_users]
test "renders sign in form", %{unauthed_conn: conn} do
test_conn = post(conn, Routes.session_path(conn, :create), session: @valid_attrs)
assert html_response(test_conn, 200) =~ "Sign In"
assert get_flash(test_conn, :error) == "Email not found."
end
end
describe "delete when user exists" do
setup [:create_user]
test "removes session", %{authed_conn: conn} do
test_conn = delete(conn, Routes.session_path(conn, :delete))
assert redirected_to(test_conn) == "/"
assert get_flash(test_conn, :info) == "Signed out successfully."
assert is_nil(get_session(test_conn, :user_id))
end
end
describe "delete when user doesn't exist" do
setup [:clear_users]
test "renders flash error", %{authed_conn: conn} do
test_conn = delete(conn, Routes.session_path(conn, :delete))
assert redirected_to(test_conn) == Routes.session_path(test_conn, :new)
assert get_flash(test_conn, :error) == "Please sign in to access that page."
assert is_nil(get_session(test_conn, :user_id))
end
end
defp create_user(_) do
user = Fixtures.user()
{:ok, user: user}
end
defp clear_users(_) do
{count, _result} = Repo.delete_all(User)
{:ok, count: count}
end
end

View File

@@ -41,7 +41,7 @@ defmodule FgHttpWeb.ConnCase do
session = Fixtures.session()
new_conn()
|> Plug.Conn.assign(:session, session)
|> Plug.Test.init_test_session(%{user_id: session.id})
end
setup tags do

View File

@@ -2,7 +2,7 @@ defmodule FgHttp.Fixtures do
@moduledoc """
Convenience helpers for inserting records
"""
alias FgHttp.{Devices, PasswordResets, Repo, Sessions, Users, Users.User}
alias FgHttp.{Devices, PasswordResets, Repo, Rules, Sessions, Users, Users.User}
def user(attrs \\ %{}) do
case Repo.get_by(User, email: "test") do
@@ -29,6 +29,16 @@ defmodule FgHttp.Fixtures do
device
end
def rule(attrs \\ %{}) do
attrs =
attrs
|> Enum.into(%{device_id: device().id})
|> Enum.into(%{destination: "0.0.0.0/0"})
{:ok, rule} = Rules.create_rule(attrs)
rule
end
def session(_attrs \\ %{}) do
email = user().email
record = Sessions.get_session!(email: email)