defmodule TarakanWeb.AccountSessionControllerTest do use TarakanWeb.ConnCase, async: true import Tarakan.AccountsFixtures alias Tarakan.Accounts setup do %{unconfirmed_account: unconfirmed_account_fixture(), account: account_fixture()} end describe "POST /accounts/log-in - handle/email and password" do test "logs the account in", %{conn: conn, account: account} do account = set_password(account) conn = post(conn, ~p"/accounts/log-in", %{ "account" => %{"identifier" => account.handle, "password" => valid_account_password()} }) assert get_session(conn, :account_token) assert redirected_to(conn) == ~p"/" # Now do a logged in request and assert on the menu conn = get(conn, ~p"/") response = html_response(conn, 200) assert response =~ "@#{account.handle}" assert response =~ ~p"/accounts/settings" assert response =~ ~p"/accounts/log-out" end test "logs the account in with remember me", %{conn: conn, account: account} do account = set_password(account) conn = post(conn, ~p"/accounts/log-in", %{ "account" => %{ "identifier" => account.email, "password" => valid_account_password(), "remember_me" => "true" } }) assert conn.resp_cookies["_tarakan_web_account_remember_me"] assert redirected_to(conn) == ~p"/" end test "logs the account in with return to", %{conn: conn, account: account} do account = set_password(account) conn = conn |> init_test_session(account_return_to: "/foo/bar") |> post(~p"/accounts/log-in", %{ "account" => %{ "identifier" => account.handle, "password" => valid_account_password() } }) assert redirected_to(conn) == "/foo/bar" assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "Welcome back!" end test "redirects to login page with invalid credentials", %{conn: conn, account: account} do conn = post(conn, ~p"/accounts/log-in?mode=password", %{ "account" => %{"identifier" => account.handle, "password" => "invalid_password"} }) assert Phoenix.Flash.get(conn.assigns.flash, :error) == "Invalid handle, email, or password" assert redirected_to(conn) == ~p"/accounts/log-in" end end describe "POST /accounts/log-in - magic link" do test "logs the account in", %{conn: conn, account: account} do {token, _hashed_token} = generate_account_magic_link_token(account) conn = post(conn, ~p"/accounts/log-in", %{ "account" => %{"token" => token} }) assert get_session(conn, :account_token) assert redirected_to(conn) == ~p"/" # Now do a logged in request and assert on the menu conn = get(conn, ~p"/") response = html_response(conn, 200) assert response =~ "@#{account.handle}" assert response =~ ~p"/accounts/settings" assert response =~ ~p"/accounts/log-out" end test "confirms unconfirmed account", %{conn: conn, unconfirmed_account: account} do {token, _hashed_token} = generate_account_magic_link_token(account) refute account.confirmed_at conn = post(conn, ~p"/accounts/log-in", %{ "account" => %{"token" => token}, "_action" => "confirmed" }) assert get_session(conn, :account_token) assert redirected_to(conn) == ~p"/" assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "Account confirmed successfully." assert Accounts.get_account!(account.id).confirmed_at # Now do a logged in request and assert on the menu conn = get(conn, ~p"/") response = html_response(conn, 200) assert response =~ "@#{account.handle}" assert response =~ ~p"/accounts/settings" assert response =~ ~p"/accounts/log-out" end test "redirects to login page when magic link is invalid", %{conn: conn} do conn = post(conn, ~p"/accounts/log-in", %{ "account" => %{"token" => "invalid"} }) assert Phoenix.Flash.get(conn.assigns.flash, :error) == "The link is invalid or it has expired." assert redirected_to(conn) == ~p"/accounts/log-in" end test "redirects to login page for a crafted non-base64 magic link", %{conn: conn} do conn = post(conn, ~p"/accounts/log-in", %{ "account" => %{"token" => "not base64!"} }) assert Phoenix.Flash.get(conn.assigns.flash, :error) == "The link is invalid or it has expired." assert redirected_to(conn) == ~p"/accounts/log-in" end end describe "POST /accounts/update-password" do test "updates the password for a confirmed account in sudo mode", %{ conn: conn, account: account } do conn = conn |> log_in_account(account) |> post(~p"/accounts/update-password", %{ "account" => %{ "password" => valid_account_password(), "password_confirmation" => valid_account_password() } }) assert redirected_to(conn) == ~p"/accounts/settings" assert Accounts.get_account_by_email_and_password(account.email, valid_account_password()) end test "redirects to re-authentication when the sudo window expired", %{ conn: conn, account: account } do stale_at = DateTime.add(DateTime.utc_now(:second), -3, :hour) conn = conn |> log_in_account(account, token_authenticated_at: stale_at) |> post(~p"/accounts/update-password", %{ "account" => %{ "password" => valid_account_password(), "password_confirmation" => valid_account_password() } }) assert redirected_to(conn) == ~p"/accounts/log-in?return_to=%2Faccounts%2Fsettings" refute Accounts.get_account_by_email_and_password(account.email, valid_account_password()) end test "refuses to establish a password before the email is confirmed", %{ conn: conn, unconfirmed_account: account } do conn = conn |> log_in_account(account) |> post(~p"/accounts/update-password", %{ "account" => %{ "password" => valid_account_password(), "password_confirmation" => valid_account_password() } }) assert redirected_to(conn) == ~p"/accounts/settings" refute Accounts.get_account!(account.id).hashed_password end end describe "DELETE /accounts/log-out" do test "logs the account out", %{conn: conn, account: account} do conn = conn |> log_in_account(account) |> delete(~p"/accounts/log-out") assert redirected_to(conn) == ~p"/" refute get_session(conn, :account_token) assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "Logged out successfully" end test "succeeds even if the account is not logged in", %{conn: conn} do conn = delete(conn, ~p"/accounts/log-out") assert redirected_to(conn) == ~p"/" refute get_session(conn, :account_token) assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "Logged out successfully" end end end