From df20935398b3f28e4f13154dc027c175073e8c22 Mon Sep 17 00:00:00 2001 From: Cam Cook Date: Sat, 16 Aug 2025 16:43:02 -0400 Subject: [PATCH 1/4] test(api): refactoring generated tests --- lib/basenji/accounts.ex | 32 ++++++++++++ lib/basenji/accounts/api_token.ex | 49 +++++++++++++++++++ lib/basenji/accounts/user_token.ex | 2 - .../confirmation_live.ex} | 2 +- .../login.ex => accounts/login_live.ex} | 2 +- .../registration_live.ex} | 2 +- .../settings.ex => accounts/settings_live.ex} | 2 +- lib/basenji_web/router.ex | 10 ++-- .../migrations/20250816193833_api_tokens.exs | 12 +++++ test/basenji/accounts_test.exs | 48 ++++++++++-------- .../user_session_controller_test.exs | 21 ++++---- .../confirmation_live_test.exs} | 2 +- .../login_live_test.exs} | 7 +-- .../registration_live_test.exs} | 4 +- .../settings_live_test.exs} | 8 +-- test/support/factory.ex | 1 + test/support/factory/accounts_factory.ex | 21 ++++++++ test/support/fixtures/accounts_fixtures.ex | 5 +- 18 files changed, 177 insertions(+), 53 deletions(-) create mode 100644 lib/basenji/accounts/api_token.ex rename lib/basenji_web/live/{user_live/confirmation.ex => accounts/confirmation_live.ex} (98%) rename lib/basenji_web/live/{user_live/login.ex => accounts/login_live.ex} (98%) rename lib/basenji_web/live/{user_live/registration.ex => accounts/registration_live.ex} (97%) rename lib/basenji_web/live/{user_live/settings.ex => accounts/settings_live.ex} (99%) create mode 100644 priv/repo/migrations/20250816193833_api_tokens.exs rename test/basenji_web/live/{user_live/confirmation_test.exs => accounts/confirmation_live_test.exs} (98%) rename test/basenji_web/live/{user_live/login_test.exs => accounts/login_live_test.exs} (94%) rename test/basenji_web/live/{user_live/registration_test.exs => accounts/registration_live_test.exs} (95%) rename test/basenji_web/live/{user_live/settings_test.exs => accounts/settings_live_test.exs} (97%) create mode 100644 test/support/factory/accounts_factory.ex diff --git a/lib/basenji/accounts.ex b/lib/basenji/accounts.ex index 7df93d3..5f0aa42 100644 --- a/lib/basenji/accounts.ex +++ b/lib/basenji/accounts.ex @@ -6,6 +6,7 @@ defmodule Basenji.Accounts do import Basenji.ContextUtils import Ecto.Query, warn: false + alias Basenji.Accounts.APIToken alias Basenji.Accounts.User alias Basenji.Accounts.UserNotifier alias Basenji.Accounts.UserToken @@ -145,6 +146,37 @@ defmodule Basenji.Accounts do :ok end + def create_api_token(%User{} = user) do + {token, user_token} = APIToken.build_api_token(user) + Repo.insert!(user_token) + token + end + + def verify_api_token(token) do + case Base.url_decode64(token, padding: false) do + {:ok, decoded_token} -> + hashed_token = :crypto.hash(APIToken.hash_algorithm(), decoded_token) + + from(t in APIToken, + where: t.token == ^hashed_token and t.inserted_at > ago(365, "day"), + join: user in assoc(t, :user), + select: user + ) + |> Repo.one() + + :error -> + :error + end + end + + def delete_api_token(token) do + from(t in APIToken, + where: t.token == ^token + ) + |> Repo.all() + |> Enum.each(&Repo.delete(&1)) + end + defp update_user_and_delete_all_tokens(changeset) do Repo.transact(fn -> with {:ok, user} <- Repo.update(changeset) do diff --git a/lib/basenji/accounts/api_token.ex b/lib/basenji/accounts/api_token.ex new file mode 100644 index 0000000..723cbd8 --- /dev/null +++ b/lib/basenji/accounts/api_token.ex @@ -0,0 +1,49 @@ +defmodule Basenji.Accounts.APIToken do + @moduledoc false + use Ecto.Schema + + import Ecto.Changeset + + alias Basenji.Accounts.APIToken + alias Basenji.Accounts.User + + @attrs [:token, :user_id] + + @hash_algorithm :sha256 + @rand_size 32 + + schema "api_tokens" do + field :token, :binary + belongs_to :user, User + + timestamps(type: :utc_datetime) + end + + def changeset(api_token, attrs) do + api_token + |> cast(attrs, @attrs) + |> validate_changeset() + end + + def build_api_token(user) do + build_hashed_token(user) + end + + defp validate_changeset(changeset) do + changeset + |> validate_required([:token, :user_id]) + end + + defp build_hashed_token(user) do + token = :crypto.strong_rand_bytes(@rand_size) + hashed_token = :crypto.hash(@hash_algorithm, token) + + {Base.url_encode64(token, padding: false), + %APIToken{ + token: hashed_token, + user_id: user.id + }} + end + + def hash_algorithm, do: @hash_algorithm +end diff --git a/lib/basenji/accounts/user_token.ex b/lib/basenji/accounts/user_token.ex index d067c8c..b8947b6 100644 --- a/lib/basenji/accounts/user_token.ex +++ b/lib/basenji/accounts/user_token.ex @@ -10,8 +10,6 @@ defmodule Basenji.Accounts.UserToken do @hash_algorithm :sha256 @rand_size 32 - # It is very important to keep the magic link token expiry short, - # since someone with access to the email may take over the account. @magic_link_validity_in_minutes 15 @change_email_validity_in_days 7 @session_validity_in_days 14 diff --git a/lib/basenji_web/live/user_live/confirmation.ex b/lib/basenji_web/live/accounts/confirmation_live.ex similarity index 98% rename from lib/basenji_web/live/user_live/confirmation.ex rename to lib/basenji_web/live/accounts/confirmation_live.ex index 71cffd4..47e6817 100644 --- a/lib/basenji_web/live/user_live/confirmation.ex +++ b/lib/basenji_web/live/accounts/confirmation_live.ex @@ -1,4 +1,4 @@ -defmodule BasenjiWeb.UserLive.Confirmation do +defmodule BasenjiWeb.Accounts.ConfirmationLive do use BasenjiWeb, :live_view alias Basenji.Accounts diff --git a/lib/basenji_web/live/user_live/login.ex b/lib/basenji_web/live/accounts/login_live.ex similarity index 98% rename from lib/basenji_web/live/user_live/login.ex rename to lib/basenji_web/live/accounts/login_live.ex index ee0260d..01fb032 100644 --- a/lib/basenji_web/live/user_live/login.ex +++ b/lib/basenji_web/live/accounts/login_live.ex @@ -1,4 +1,4 @@ -defmodule BasenjiWeb.UserLive.Login do +defmodule BasenjiWeb.Accounts.LoginLive do use BasenjiWeb, :live_view alias Basenji.Accounts diff --git a/lib/basenji_web/live/user_live/registration.ex b/lib/basenji_web/live/accounts/registration_live.ex similarity index 97% rename from lib/basenji_web/live/user_live/registration.ex rename to lib/basenji_web/live/accounts/registration_live.ex index ef703ae..6a5de7e 100644 --- a/lib/basenji_web/live/user_live/registration.ex +++ b/lib/basenji_web/live/accounts/registration_live.ex @@ -1,4 +1,4 @@ -defmodule BasenjiWeb.UserLive.Registration do +defmodule BasenjiWeb.Accounts.RegistrationLive do use BasenjiWeb, :live_view alias Basenji.Accounts diff --git a/lib/basenji_web/live/user_live/settings.ex b/lib/basenji_web/live/accounts/settings_live.ex similarity index 99% rename from lib/basenji_web/live/user_live/settings.ex rename to lib/basenji_web/live/accounts/settings_live.ex index 8dbab16..3de616c 100644 --- a/lib/basenji_web/live/user_live/settings.ex +++ b/lib/basenji_web/live/accounts/settings_live.ex @@ -1,4 +1,4 @@ -defmodule BasenjiWeb.UserLive.Settings do +defmodule BasenjiWeb.Accounts.SettingsLive do use BasenjiWeb, :live_view alias Basenji.Accounts diff --git a/lib/basenji_web/router.ex b/lib/basenji_web/router.ex index d3a63d8..5aefb76 100644 --- a/lib/basenji_web/router.ex +++ b/lib/basenji_web/router.ex @@ -119,8 +119,8 @@ defmodule BasenjiWeb.Router do live_session :require_authenticated_user, on_mount: [{BasenjiWeb.UserAuth, :require_authenticated}] do - live "/users/settings", UserLive.Settings, :edit - live "/users/settings/confirm-email/:token", UserLive.Settings, :confirm_email + live "/users/settings", Accounts.SettingsLive, :edit + live "/users/settings/confirm-email/:token", Accounts.SettingsLive, :confirm_email end post "/users/update-password", UserSessionController, :update_password @@ -131,9 +131,9 @@ defmodule BasenjiWeb.Router do live_session :current_user, on_mount: [{BasenjiWeb.UserAuth, :mount_current_scope}] do - live "/users/register", UserLive.Registration, :new - live "/users/log-in", UserLive.Login, :new - live "/users/log-in/:token", UserLive.Confirmation, :new + live "/users/register", Accounts.RegistrationLive, :new + live "/users/log-in", Accounts.LoginLive, :new + live "/users/log-in/:token", Accounts.ConfirmationLive, :new end post "/users/log-in", UserSessionController, :create diff --git a/priv/repo/migrations/20250816193833_api_tokens.exs b/priv/repo/migrations/20250816193833_api_tokens.exs new file mode 100644 index 0000000..1d84536 --- /dev/null +++ b/priv/repo/migrations/20250816193833_api_tokens.exs @@ -0,0 +1,12 @@ +defmodule Basenji.Repo.Migrations.ApiTokens do + use Ecto.Migration + + def change do + create table(:api_tokens) do + add :user_id, references(:users, on_delete: :delete_all), null: false + add :token, :binary, null: false + + timestamps(type: :utc_datetime) + end + end +end diff --git a/test/basenji/accounts_test.exs b/test/basenji/accounts_test.exs index 9eb49a1..e05e686 100644 --- a/test/basenji/accounts_test.exs +++ b/test/basenji/accounts_test.exs @@ -6,13 +6,21 @@ defmodule Basenji.AccountsTest do alias Basenji.Accounts alias Basenji.Accounts.{User, UserToken} + test "CRUD API tokens" do + %{id: id} = user = insert(:user) + api_token = Accounts.create_api_token(user) + assert api_token + assert id == Accounts.verify_api_token(api_token).id + assert :ok == Accounts.delete_api_token(api_token) + end + describe "get_user_by_email/1" do test "does not return the user if the email does not exist" do assert Enum.empty?(Accounts.list_users(email: "unknown@example.com")) end test "returns the user if the email exists" do - %{id: id} = user = user_fixture() + %{id: id} = user = insert(:user) assert [%User{id: ^id}] = Accounts.list_users(email: user.email) end end @@ -23,21 +31,22 @@ defmodule Basenji.AccountsTest do end test "does not return the user if the password is not valid" do - user = user_fixture() |> set_password() + user = insert(:user) |> set_password() refute Accounts.get_user_by_email_and_password(user.email, "invalid") end test "returns the user if the email and password are valid" do - %{id: id} = user = user_fixture() |> set_password() + password = Faker.Internet.slug() + %{id: id} = user = insert(:user, password: password) assert %User{id: ^id} = - Accounts.get_user_by_email_and_password(user.email, valid_user_password()) + Accounts.get_user_by_email_and_password(user.email, password) end end describe "get_user!/1" do test "returns the user with the given id" do - %{id: id} = user = user_fixture() + %{id: id} = user = insert(:user) assert {:ok, %User{id: ^id}} = Accounts.get_user(user.id) end end @@ -62,7 +71,7 @@ defmodule Basenji.AccountsTest do end test "validates email uniqueness" do - %{email: email} = user_fixture() + %{email: email} = insert(:user) {:error, changeset} = Accounts.register_user(%{email: email}) assert "has already been taken" in errors_on(changeset).email @@ -72,7 +81,7 @@ defmodule Basenji.AccountsTest do end test "registers users without password" do - email = unique_user_email() + email = Faker.Internet.email() {:ok, user} = Accounts.register_user(valid_user_attributes(email: email)) assert user.email == email assert is_nil(user.hashed_password) @@ -109,7 +118,7 @@ defmodule Basenji.AccountsTest do describe "deliver_user_update_email_instructions/3" do setup do - %{user: user_fixture()} + %{user: insert(:user)} end test "sends token through notification", %{user: user} do @@ -128,8 +137,8 @@ defmodule Basenji.AccountsTest do describe "update_user_email/2" do setup do - user = unconfirmed_user_fixture() - email = unique_user_email() + user = insert(:user) + email = Faker.Internet.email() token = extract_user_token(fn url -> @@ -198,7 +207,7 @@ defmodule Basenji.AccountsTest do describe "update_user_password/2" do setup do - %{user: user_fixture()} + %{user: insert(:user)} end test "validates password", %{user: user} do @@ -230,7 +239,6 @@ defmodule Basenji.AccountsTest do }) assert expired_tokens == [] - assert is_nil(user.password) assert Accounts.get_user_by_email_and_password(user.email, "new valid password") end @@ -248,7 +256,7 @@ defmodule Basenji.AccountsTest do describe "generate_user_session_token/1" do setup do - %{user: user_fixture()} + %{user: insert(:user)} end test "generates a token", %{user: user} do @@ -261,7 +269,7 @@ defmodule Basenji.AccountsTest do assert_raise Ecto.ConstraintError, fn -> Repo.insert!(%UserToken{ token: user_token.token, - user_id: user_fixture().id, + user_id: insert(:user).id, context: "session" }) end @@ -278,7 +286,7 @@ defmodule Basenji.AccountsTest do describe "get_user_by_session_token/1" do setup do - user = user_fixture() + user = insert(:user) token = Accounts.generate_user_session_token(user) %{user: user, token: token} end @@ -303,7 +311,7 @@ defmodule Basenji.AccountsTest do describe "get_user_by_magic_link_token/1" do setup do - user = user_fixture() + user = insert(:user) {encoded_token, _hashed_token} = generate_user_magic_link_token(user) %{user: user, token: encoded_token} end @@ -325,7 +333,7 @@ defmodule Basenji.AccountsTest do describe "login_user_by_magic_link/1" do test "confirms user and expires tokens" do - user = unconfirmed_user_fixture() + user = insert(:user, confirmed_at: nil, hashed_password: nil, password: nil) refute user.confirmed_at {encoded_token, hashed_token} = generate_user_magic_link_token(user) @@ -345,7 +353,7 @@ defmodule Basenji.AccountsTest do end test "raises when unconfirmed user has password set" do - user = unconfirmed_user_fixture() + user = insert(:user, confirmed_at: nil) {1, nil} = Repo.update_all(User, set: [hashed_password: "hashed"]) {encoded_token, _hashed_token} = generate_user_magic_link_token(user) @@ -357,7 +365,7 @@ defmodule Basenji.AccountsTest do describe "delete_user_session_token/1" do test "deletes the token" do - user = user_fixture() + user = insert(:user) token = Accounts.generate_user_session_token(user) assert Accounts.delete_user_session_token(token) == :ok refute Accounts.get_user_by_session_token(token) @@ -366,7 +374,7 @@ defmodule Basenji.AccountsTest do describe "deliver_login_instructions/2" do setup do - %{user: unconfirmed_user_fixture()} + %{user: insert(:user)} end test "sends token through notification", %{user: user} do diff --git a/test/basenji_web/controllers/user_session_controller_test.exs b/test/basenji_web/controllers/user_session_controller_test.exs index 7b1cbbf..c0af78c 100644 --- a/test/basenji_web/controllers/user_session_controller_test.exs +++ b/test/basenji_web/controllers/user_session_controller_test.exs @@ -10,12 +10,13 @@ defmodule BasenjiWeb.UserSessionControllerTest do end describe "POST /users/log-in - email and password" do - test "logs the user in", %{conn: conn, user: user} do - user = set_password(user) + test "logs the user in", %{conn: conn, user: _user} do + password = Faker.Internet.slug() + user = insert(:user, password: password) conn = post(conn, ~p"/users/log-in", %{ - "user" => %{"email" => user.email, "password" => valid_user_password()} + "user" => %{"email" => user.email, "password" => password} }) assert get_session(conn, :user_token) @@ -29,14 +30,15 @@ defmodule BasenjiWeb.UserSessionControllerTest do assert response =~ ~p"/users/log-out" end - test "logs the user in with remember me", %{conn: conn, user: user} do - user = set_password(user) + test "logs the user in with remember me", %{conn: conn, user: _user} do + password = Faker.Internet.slug() + user = insert(:user, password: password) conn = post(conn, ~p"/users/log-in", %{ "user" => %{ "email" => user.email, - "password" => valid_user_password(), + "password" => password, "remember_me" => "true" } }) @@ -45,8 +47,9 @@ defmodule BasenjiWeb.UserSessionControllerTest do assert redirected_to(conn) == ~p"/" end - test "logs the user in with return to", %{conn: conn, user: user} do - user = set_password(user) + test "logs the user in with return to", %{conn: conn, user: _user} do + password = Faker.Internet.slug() + user = insert(:user, password: password) conn = conn @@ -54,7 +57,7 @@ defmodule BasenjiWeb.UserSessionControllerTest do |> post(~p"/users/log-in", %{ "user" => %{ "email" => user.email, - "password" => valid_user_password() + "password" => password } }) diff --git a/test/basenji_web/live/user_live/confirmation_test.exs b/test/basenji_web/live/accounts/confirmation_live_test.exs similarity index 98% rename from test/basenji_web/live/user_live/confirmation_test.exs rename to test/basenji_web/live/accounts/confirmation_live_test.exs index 2c7f9c9..85c569f 100644 --- a/test/basenji_web/live/user_live/confirmation_test.exs +++ b/test/basenji_web/live/accounts/confirmation_live_test.exs @@ -1,4 +1,4 @@ -defmodule BasenjiWeb.UserLive.ConfirmationTest do +defmodule BasenjiWeb.Accounts.ConfirmationLiveTest do use BasenjiWeb.ConnCase, async: true import Basenji.AccountsFixtures diff --git a/test/basenji_web/live/user_live/login_test.exs b/test/basenji_web/live/accounts/login_live_test.exs similarity index 94% rename from test/basenji_web/live/user_live/login_test.exs rename to test/basenji_web/live/accounts/login_live_test.exs index 0b4ec4e..0b70c3e 100644 --- a/test/basenji_web/live/user_live/login_test.exs +++ b/test/basenji_web/live/accounts/login_live_test.exs @@ -1,4 +1,4 @@ -defmodule BasenjiWeb.UserLive.LoginTest do +defmodule BasenjiWeb.Accounts.LoginLiveTest do use BasenjiWeb.ConnCase, async: true import Basenji.AccountsFixtures @@ -47,12 +47,13 @@ defmodule BasenjiWeb.UserLive.LoginTest do describe "user login - password" do test "redirects if user logs in with valid credentials", %{conn: conn} do - user = user_fixture() |> set_password() + password = Faker.Internet.slug() + user = insert(:user, password: password) {:ok, lv, _html} = live(conn, ~p"/users/log-in") form = - form(lv, "#login_form_password", user: %{email: user.email, password: valid_user_password(), remember_me: true}) + form(lv, "#login_form_password", user: %{email: user.email, password: password, remember_me: true}) conn = submit_form(form, conn) diff --git a/test/basenji_web/live/user_live/registration_test.exs b/test/basenji_web/live/accounts/registration_live_test.exs similarity index 95% rename from test/basenji_web/live/user_live/registration_test.exs rename to test/basenji_web/live/accounts/registration_live_test.exs index 346e5d6..489f8da 100644 --- a/test/basenji_web/live/user_live/registration_test.exs +++ b/test/basenji_web/live/accounts/registration_live_test.exs @@ -1,4 +1,4 @@ -defmodule BasenjiWeb.UserLive.RegistrationTest do +defmodule BasenjiWeb.Accounts.RegistrationLiveTest do use BasenjiWeb.ConnCase, async: true import Basenji.AccountsFixtures @@ -39,7 +39,7 @@ defmodule BasenjiWeb.UserLive.RegistrationTest do test "creates account but does not log in", %{conn: conn} do {:ok, lv, _html} = live(conn, ~p"/users/register") - email = unique_user_email() + email = Faker.Internet.email() form = form(lv, "#registration_form", user: valid_user_attributes(email: email)) {:ok, _lv, html} = diff --git a/test/basenji_web/live/user_live/settings_test.exs b/test/basenji_web/live/accounts/settings_live_test.exs similarity index 97% rename from test/basenji_web/live/user_live/settings_test.exs rename to test/basenji_web/live/accounts/settings_live_test.exs index dc50819..fc0d2f3 100644 --- a/test/basenji_web/live/user_live/settings_test.exs +++ b/test/basenji_web/live/accounts/settings_live_test.exs @@ -1,4 +1,4 @@ -defmodule BasenjiWeb.UserLive.SettingsTest do +defmodule BasenjiWeb.Accounts.SettingsLiveTest do use BasenjiWeb.ConnCase, async: true import Basenji.AccountsFixtures @@ -45,7 +45,7 @@ defmodule BasenjiWeb.UserLive.SettingsTest do end test "updates the user email", %{conn: conn, user: user} do - new_email = unique_user_email() + new_email = Faker.Internet.email() {:ok, lv, _html} = live(conn, ~p"/users/settings") @@ -97,7 +97,7 @@ defmodule BasenjiWeb.UserLive.SettingsTest do end test "updates the user password", %{conn: conn, user: user} do - new_password = valid_user_password() + new_password = Faker.Internet.slug() {:ok, lv, _html} = live(conn, ~p"/users/settings") @@ -164,7 +164,7 @@ defmodule BasenjiWeb.UserLive.SettingsTest do describe "confirm email" do setup %{conn: conn} do user = user_fixture() - email = unique_user_email() + email = Faker.Internet.email() token = extract_user_token(fn url -> diff --git a/test/support/factory.ex b/test/support/factory.ex index d44ced6..e521e6c 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -1,6 +1,7 @@ defmodule Basenji.Factory do @moduledoc false use ExMachina.Ecto, repo: Basenji.Repo + use Basenji.Factory.AccountsFactory use Basenji.Factory.ComicFactory use Basenji.Factory.CollectionFactory end diff --git a/test/support/factory/accounts_factory.ex b/test/support/factory/accounts_factory.ex new file mode 100644 index 0000000..68849c0 --- /dev/null +++ b/test/support/factory/accounts_factory.ex @@ -0,0 +1,21 @@ +defmodule Basenji.Factory.AccountsFactory do + @moduledoc false + + defmacro __using__(_opts) do + quote do + def user_factory(attrs) do + password = Map.get(attrs, :password, Faker.Internet.slug()) + hashed = if password, do: Bcrypt.hash_pwd_salt(password) + + %Basenji.Accounts.User{ + email: Faker.Internet.email(), + password: password, + hashed_password: hashed, + confirmed_at: Faker.DateTime.backward(1) + } + |> merge_attributes(attrs) + |> evaluate_lazy_attributes() + end + end + end +end diff --git a/test/support/fixtures/accounts_fixtures.ex b/test/support/fixtures/accounts_fixtures.ex index ad706d6..1cba61c 100644 --- a/test/support/fixtures/accounts_fixtures.ex +++ b/test/support/fixtures/accounts_fixtures.ex @@ -9,12 +9,11 @@ defmodule Basenji.AccountsFixtures do alias Basenji.Accounts alias Basenji.Accounts.Scope - def unique_user_email, do: "user#{System.unique_integer()}@example.com" def valid_user_password, do: "hello world!" def valid_user_attributes(attrs \\ %{}) do Enum.into(attrs, %{ - email: unique_user_email() + email: Faker.Internet.email() }) end @@ -52,7 +51,7 @@ defmodule Basenji.AccountsFixtures do def set_password(user) do {:ok, {user, _expired_tokens}} = - Accounts.update_user_password(user, %{password: valid_user_password()}) + Accounts.update_user_password(user, %{password: Faker.Internet.slug()}) user end From 82efd45636ef36fefc9c730a90bdb41ea742e291 Mon Sep 17 00:00:00 2001 From: Cam Cook Date: Sat, 16 Aug 2025 17:08:59 -0400 Subject: [PATCH 2/4] test(api): refactoring generated tests --- test/basenji/accounts_test.exs | 9 +-- .../user_session_controller_test.exs | 2 +- .../live/accounts/confirmation_live_test.exs | 2 +- .../live/accounts/login_live_test.exs | 5 +- .../live/accounts/registration_live_test.exs | 13 ++-- .../live/accounts/settings_live_test.exs | 16 ++--- test/basenji_web/user_auth_test.exs | 4 +- test/support/conn_case.ex | 4 +- test/support/fixtures/accounts_fixtures.ex | 64 +++++-------------- 9 files changed, 47 insertions(+), 72 deletions(-) diff --git a/test/basenji/accounts_test.exs b/test/basenji/accounts_test.exs index e05e686..f74bfde 100644 --- a/test/basenji/accounts_test.exs +++ b/test/basenji/accounts_test.exs @@ -31,7 +31,7 @@ defmodule Basenji.AccountsTest do end test "does not return the user if the password is not valid" do - user = insert(:user) |> set_password() + user = insert(:user) refute Accounts.get_user_by_email_and_password(user.email, "invalid") end @@ -82,7 +82,7 @@ defmodule Basenji.AccountsTest do test "registers users without password" do email = Faker.Internet.email() - {:ok, user} = Accounts.register_user(valid_user_attributes(email: email)) + user = insert(:user, email: email, hashed_password: nil, confirmed_at: nil, password: nil) assert user.email == email assert is_nil(user.hashed_password) assert is_nil(user.confirmed_at) @@ -344,10 +344,11 @@ defmodule Basenji.AccountsTest do end test "returns user and (deleted) token for confirmed user" do - user = user_fixture() + %{id: user_id} = user = insert(:user) assert user.confirmed_at {encoded_token, _hashed_token} = generate_user_magic_link_token(user) - assert {:ok, {^user, []}} = Accounts.login_user_by_magic_link(encoded_token) + assert {:ok, {retrieved, []}} = Accounts.login_user_by_magic_link(encoded_token) + assert retrieved.id == user_id # one time use only assert {:error, :not_found} = Accounts.login_user_by_magic_link(encoded_token) end diff --git a/test/basenji_web/controllers/user_session_controller_test.exs b/test/basenji_web/controllers/user_session_controller_test.exs index c0af78c..aa3eca3 100644 --- a/test/basenji_web/controllers/user_session_controller_test.exs +++ b/test/basenji_web/controllers/user_session_controller_test.exs @@ -6,7 +6,7 @@ defmodule BasenjiWeb.UserSessionControllerTest do alias Basenji.Accounts setup do - %{unconfirmed_user: unconfirmed_user_fixture(), user: user_fixture()} + %{unconfirmed_user: insert(:user, confirmed_at: nil, password: nil), user: insert(:user)} end describe "POST /users/log-in - email and password" do diff --git a/test/basenji_web/live/accounts/confirmation_live_test.exs b/test/basenji_web/live/accounts/confirmation_live_test.exs index 85c569f..7650fc1 100644 --- a/test/basenji_web/live/accounts/confirmation_live_test.exs +++ b/test/basenji_web/live/accounts/confirmation_live_test.exs @@ -7,7 +7,7 @@ defmodule BasenjiWeb.Accounts.ConfirmationLiveTest do alias Basenji.Accounts setup do - %{unconfirmed_user: unconfirmed_user_fixture(), confirmed_user: user_fixture()} + %{unconfirmed_user: insert(:user, confirmed_at: nil, password: nil), confirmed_user: insert(:user)} end describe "Confirm user" do diff --git a/test/basenji_web/live/accounts/login_live_test.exs b/test/basenji_web/live/accounts/login_live_test.exs index 0b70c3e..ad353a7 100644 --- a/test/basenji_web/live/accounts/login_live_test.exs +++ b/test/basenji_web/live/accounts/login_live_test.exs @@ -1,7 +1,6 @@ defmodule BasenjiWeb.Accounts.LoginLiveTest do use BasenjiWeb.ConnCase, async: true - import Basenji.AccountsFixtures import Phoenix.LiveViewTest alias Basenji.Accounts.UserToken @@ -18,7 +17,7 @@ defmodule BasenjiWeb.Accounts.LoginLiveTest do describe "user login - magic link" do test "sends magic link email when user exists", %{conn: conn} do - user = user_fixture() + user = insert(:user) {:ok, lv, _html} = live(conn, ~p"/users/log-in") @@ -92,7 +91,7 @@ defmodule BasenjiWeb.Accounts.LoginLiveTest do describe "re-authentication (sudo mode)" do setup %{conn: conn} do - user = user_fixture() + user = insert(:user) %{user: user, conn: log_in_user(conn, user)} end diff --git a/test/basenji_web/live/accounts/registration_live_test.exs b/test/basenji_web/live/accounts/registration_live_test.exs index 489f8da..f2d0c2d 100644 --- a/test/basenji_web/live/accounts/registration_live_test.exs +++ b/test/basenji_web/live/accounts/registration_live_test.exs @@ -1,7 +1,6 @@ defmodule BasenjiWeb.Accounts.RegistrationLiveTest do use BasenjiWeb.ConnCase, async: true - import Basenji.AccountsFixtures import Phoenix.LiveViewTest describe "Registration page" do @@ -15,7 +14,7 @@ defmodule BasenjiWeb.Accounts.RegistrationLiveTest do test "redirects if already logged in", %{conn: conn} do result = conn - |> log_in_user(user_fixture()) + |> log_in_user(insert(:user)) |> live(~p"/users/register") |> follow_redirect(conn, ~p"/") @@ -40,7 +39,13 @@ defmodule BasenjiWeb.Accounts.RegistrationLiveTest do {:ok, lv, _html} = live(conn, ~p"/users/register") email = Faker.Internet.email() - form = form(lv, "#registration_form", user: valid_user_attributes(email: email)) + + form = + form(lv, "#registration_form", + user: %{ + email: email + } + ) {:ok, _lv, html} = render_submit(form) @@ -53,7 +58,7 @@ defmodule BasenjiWeb.Accounts.RegistrationLiveTest do test "renders errors for duplicated email", %{conn: conn} do {:ok, lv, _html} = live(conn, ~p"/users/register") - user = user_fixture(%{email: "test@email.com"}) + user = insert(:user) result = lv diff --git a/test/basenji_web/live/accounts/settings_live_test.exs b/test/basenji_web/live/accounts/settings_live_test.exs index fc0d2f3..c34ff65 100644 --- a/test/basenji_web/live/accounts/settings_live_test.exs +++ b/test/basenji_web/live/accounts/settings_live_test.exs @@ -10,7 +10,7 @@ defmodule BasenjiWeb.Accounts.SettingsLiveTest do test "renders settings page", %{conn: conn} do {:ok, _lv, html} = conn - |> log_in_user(user_fixture()) + |> log_in_user(insert(:user)) |> live(~p"/users/settings") assert html =~ "Change Email" @@ -28,7 +28,7 @@ defmodule BasenjiWeb.Accounts.SettingsLiveTest do test "redirects if user is not in sudo mode", %{conn: conn} do {:ok, conn} = conn - |> log_in_user(user_fixture(), + |> log_in_user(insert(:user), token_authenticated_at: DateTime.add(DateTime.utc_now(:second), -11, :minute) ) |> live(~p"/users/settings") @@ -40,7 +40,7 @@ defmodule BasenjiWeb.Accounts.SettingsLiveTest do describe "update email form" do setup %{conn: conn} do - user = user_fixture() + user = insert(:user) %{conn: log_in_user(conn, user), user: user} end @@ -92,7 +92,7 @@ defmodule BasenjiWeb.Accounts.SettingsLiveTest do describe "update password form" do setup %{conn: conn} do - user = user_fixture() + user = insert(:user) %{conn: log_in_user(conn, user), user: user} end @@ -163,15 +163,15 @@ defmodule BasenjiWeb.Accounts.SettingsLiveTest do describe "confirm email" do setup %{conn: conn} do - user = user_fixture() - email = Faker.Internet.email() + user = insert(:user) + new_email = Faker.Internet.email() token = extract_user_token(fn url -> - Accounts.deliver_user_update_email_instructions(%{user | email: email}, user.email, url) + Accounts.deliver_user_update_email_instructions(%{user | email: new_email}, user.email, url) end) - %{conn: log_in_user(conn, user), token: token, email: email, user: user} + %{conn: log_in_user(conn, user), token: token, email: new_email, user: user} end test "updates the user email once", %{conn: conn, user: user, token: token, email: email} do diff --git a/test/basenji_web/user_auth_test.exs b/test/basenji_web/user_auth_test.exs index 161314a..6fb0ab9 100644 --- a/test/basenji_web/user_auth_test.exs +++ b/test/basenji_web/user_auth_test.exs @@ -18,7 +18,7 @@ defmodule BasenjiWeb.UserAuthTest do |> Map.replace!(:secret_key_base, BasenjiWeb.Endpoint.config(:secret_key_base)) |> init_test_session(%{}) - %{user: %{user_fixture() | authenticated_at: DateTime.utc_now(:second)}, conn: conn} + %{user: %{insert(:user) | authenticated_at: DateTime.utc_now(:second)}, conn: conn} end describe "log_in_user/3" do @@ -49,7 +49,7 @@ defmodule BasenjiWeb.UserAuthTest do conn: conn, user: user } do - other_user = user_fixture() + other_user = insert(:user) conn = conn diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index 5113cb9..1051dfd 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -18,6 +18,7 @@ defmodule BasenjiWeb.ConnCase do use ExUnit.CaseTemplate alias Basenji.Accounts.Scope + alias Basenji.Accounts.User using do quote do @@ -48,7 +49,8 @@ defmodule BasenjiWeb.ConnCase do test context. """ def register_and_log_in_user(%{conn: conn} = context) do - user = Basenji.AccountsFixtures.user_fixture() + # insert(:user) + user = %User{email: Faker.Internet.email()} scope = Scope.for_user(user) opts = diff --git a/test/support/fixtures/accounts_fixtures.ex b/test/support/fixtures/accounts_fixtures.ex index 1cba61c..b7d54e2 100644 --- a/test/support/fixtures/accounts_fixtures.ex +++ b/test/support/fixtures/accounts_fixtures.ex @@ -7,54 +7,22 @@ defmodule Basenji.AccountsFixtures do import Ecto.Query alias Basenji.Accounts - alias Basenji.Accounts.Scope - - def valid_user_password, do: "hello world!" - - def valid_user_attributes(attrs \\ %{}) do - Enum.into(attrs, %{ - email: Faker.Internet.email() - }) - end - - def unconfirmed_user_fixture(attrs \\ %{}) do - {:ok, user} = - attrs - |> valid_user_attributes() - |> Accounts.register_user() - - user - end - - def user_fixture(attrs \\ %{}) do - user = unconfirmed_user_fixture(attrs) - - token = - extract_user_token(fn url -> - Accounts.deliver_login_instructions(user, url) - end) - - {:ok, {user, _expired_tokens}} = - Accounts.login_user_by_magic_link(token) - - user - end - - def user_scope_fixture do - user = user_fixture() - user_scope_fixture(user) - end - - def user_scope_fixture(user) do - Scope.for_user(user) - end - - def set_password(user) do - {:ok, {user, _expired_tokens}} = - Accounts.update_user_password(user, %{password: Faker.Internet.slug()}) - - user - end + # alias Basenji.Accounts.Scope + # alias Basenji.Accounts.User + + # def user_fixture(attrs \\ %{}) do + # user = %{} + # + # token = + # extract_user_token(fn url -> + # Accounts.deliver_login_instructions(user, url) + # end) + # + # {:ok, {user, _expired_tokens}} = + # Accounts.login_user_by_magic_link(token) + # + # user + # end def extract_user_token(fun) do {:ok, captured_email} = fun.(&"[TOKEN]#{&1}[TOKEN]") From f9373a9c4101290200cb5dbc87480039a2b753c7 Mon Sep 17 00:00:00 2001 From: Cam Cook Date: Sat, 16 Aug 2025 17:21:41 -0400 Subject: [PATCH 3/4] test(api): refactoring generated tests --- test/basenji/accounts_test.exs | 16 +++--- .../user_session_controller_test.exs | 6 +- .../live/accounts/confirmation_live_test.exs | 9 ++- .../live/accounts/settings_live_test.exs | 3 +- test/basenji_web/user_auth_test.exs | 12 +++- test/support/conn_case.ex | 10 +++- test/support/fixtures/accounts_fixtures.ex | 56 ------------------- test/test_helper.exs | 14 +++++ 8 files changed, 48 insertions(+), 78 deletions(-) delete mode 100644 test/support/fixtures/accounts_fixtures.ex diff --git a/test/basenji/accounts_test.exs b/test/basenji/accounts_test.exs index f74bfde..eaa6e70 100644 --- a/test/basenji/accounts_test.exs +++ b/test/basenji/accounts_test.exs @@ -1,8 +1,6 @@ defmodule Basenji.AccountsTest do use Basenji.DataCase - import Basenji.AccountsFixtures - alias Basenji.Accounts alias Basenji.Accounts.{User, UserToken} @@ -123,7 +121,7 @@ defmodule Basenji.AccountsTest do test "sends token through notification", %{user: user} do token = - extract_user_token(fn url -> + TestHelper.extract_user_token(fn url -> Accounts.deliver_user_update_email_instructions(user, "current@example.com", url) end) @@ -141,7 +139,7 @@ defmodule Basenji.AccountsTest do email = Faker.Internet.email() token = - extract_user_token(fn url -> + TestHelper.extract_user_token(fn url -> Accounts.deliver_user_update_email_instructions(%{user | email: email}, user.email, url) end) @@ -312,7 +310,7 @@ defmodule Basenji.AccountsTest do describe "get_user_by_magic_link_token/1" do setup do user = insert(:user) - {encoded_token, _hashed_token} = generate_user_magic_link_token(user) + {encoded_token, _hashed_token} = TestHelper.generate_user_magic_link_token(user) %{user: user, token: encoded_token} end @@ -335,7 +333,7 @@ defmodule Basenji.AccountsTest do test "confirms user and expires tokens" do user = insert(:user, confirmed_at: nil, hashed_password: nil, password: nil) refute user.confirmed_at - {encoded_token, hashed_token} = generate_user_magic_link_token(user) + {encoded_token, hashed_token} = TestHelper.generate_user_magic_link_token(user) assert {:ok, {user, [%{token: ^hashed_token}]}} = Accounts.login_user_by_magic_link(encoded_token) @@ -346,7 +344,7 @@ defmodule Basenji.AccountsTest do test "returns user and (deleted) token for confirmed user" do %{id: user_id} = user = insert(:user) assert user.confirmed_at - {encoded_token, _hashed_token} = generate_user_magic_link_token(user) + {encoded_token, _hashed_token} = TestHelper.generate_user_magic_link_token(user) assert {:ok, {retrieved, []}} = Accounts.login_user_by_magic_link(encoded_token) assert retrieved.id == user_id # one time use only @@ -356,7 +354,7 @@ defmodule Basenji.AccountsTest do test "raises when unconfirmed user has password set" do user = insert(:user, confirmed_at: nil) {1, nil} = Repo.update_all(User, set: [hashed_password: "hashed"]) - {encoded_token, _hashed_token} = generate_user_magic_link_token(user) + {encoded_token, _hashed_token} = TestHelper.generate_user_magic_link_token(user) assert_raise RuntimeError, ~r/magic link log in is not allowed/, fn -> Accounts.login_user_by_magic_link(encoded_token) @@ -380,7 +378,7 @@ defmodule Basenji.AccountsTest do test "sends token through notification", %{user: user} do token = - extract_user_token(fn url -> + TestHelper.extract_user_token(fn url -> Accounts.deliver_login_instructions(user, url) end) diff --git a/test/basenji_web/controllers/user_session_controller_test.exs b/test/basenji_web/controllers/user_session_controller_test.exs index aa3eca3..e6d6200 100644 --- a/test/basenji_web/controllers/user_session_controller_test.exs +++ b/test/basenji_web/controllers/user_session_controller_test.exs @@ -1,8 +1,6 @@ defmodule BasenjiWeb.UserSessionControllerTest do use BasenjiWeb.ConnCase, async: true - import Basenji.AccountsFixtures - alias Basenji.Accounts setup do @@ -78,7 +76,7 @@ defmodule BasenjiWeb.UserSessionControllerTest do describe "POST /users/log-in - magic link" do test "logs the user in", %{conn: conn, user: user} do - {token, _hashed_token} = generate_user_magic_link_token(user) + {token, _hashed_token} = TestHelper.generate_user_magic_link_token(user) conn = post(conn, ~p"/users/log-in", %{ @@ -97,7 +95,7 @@ defmodule BasenjiWeb.UserSessionControllerTest do end test "confirms unconfirmed user", %{conn: conn, unconfirmed_user: user} do - {token, _hashed_token} = generate_user_magic_link_token(user) + {token, _hashed_token} = TestHelper.generate_user_magic_link_token(user) refute user.confirmed_at conn = diff --git a/test/basenji_web/live/accounts/confirmation_live_test.exs b/test/basenji_web/live/accounts/confirmation_live_test.exs index 7650fc1..e4ff90b 100644 --- a/test/basenji_web/live/accounts/confirmation_live_test.exs +++ b/test/basenji_web/live/accounts/confirmation_live_test.exs @@ -1,7 +1,6 @@ defmodule BasenjiWeb.Accounts.ConfirmationLiveTest do use BasenjiWeb.ConnCase, async: true - import Basenji.AccountsFixtures import Phoenix.LiveViewTest alias Basenji.Accounts @@ -13,7 +12,7 @@ defmodule BasenjiWeb.Accounts.ConfirmationLiveTest do describe "Confirm user" do test "renders confirmation page for unconfirmed user", %{conn: conn, unconfirmed_user: user} do token = - extract_user_token(fn url -> + TestHelper.extract_user_token(fn url -> Accounts.deliver_login_instructions(user, url) end) @@ -23,7 +22,7 @@ defmodule BasenjiWeb.Accounts.ConfirmationLiveTest do test "renders login page for confirmed user", %{conn: conn, confirmed_user: user} do token = - extract_user_token(fn url -> + TestHelper.extract_user_token(fn url -> Accounts.deliver_login_instructions(user, url) end) @@ -34,7 +33,7 @@ defmodule BasenjiWeb.Accounts.ConfirmationLiveTest do test "confirms the given token once", %{conn: conn, unconfirmed_user: user} do token = - extract_user_token(fn url -> + TestHelper.extract_user_token(fn url -> Accounts.deliver_login_instructions(user, url) end) @@ -69,7 +68,7 @@ defmodule BasenjiWeb.Accounts.ConfirmationLiveTest do confirmed_user: user } do token = - extract_user_token(fn url -> + TestHelper.extract_user_token(fn url -> Accounts.deliver_login_instructions(user, url) end) diff --git a/test/basenji_web/live/accounts/settings_live_test.exs b/test/basenji_web/live/accounts/settings_live_test.exs index c34ff65..783752a 100644 --- a/test/basenji_web/live/accounts/settings_live_test.exs +++ b/test/basenji_web/live/accounts/settings_live_test.exs @@ -1,7 +1,6 @@ defmodule BasenjiWeb.Accounts.SettingsLiveTest do use BasenjiWeb.ConnCase, async: true - import Basenji.AccountsFixtures import Phoenix.LiveViewTest alias Basenji.Accounts @@ -167,7 +166,7 @@ defmodule BasenjiWeb.Accounts.SettingsLiveTest do new_email = Faker.Internet.email() token = - extract_user_token(fn url -> + TestHelper.extract_user_token(fn url -> Accounts.deliver_user_update_email_instructions(%{user | email: new_email}, user.email, url) end) diff --git a/test/basenji_web/user_auth_test.exs b/test/basenji_web/user_auth_test.exs index 6fb0ab9..75ad51a 100644 --- a/test/basenji_web/user_auth_test.exs +++ b/test/basenji_web/user_auth_test.exs @@ -1,10 +1,11 @@ defmodule BasenjiWeb.UserAuthTest do use BasenjiWeb.ConnCase, async: true - import Basenji.AccountsFixtures + import Ecto.Query alias Basenji.Accounts alias Basenji.Accounts.Scope + alias Basenji.Accounts.UserToken alias BasenjiWeb.UserAuth alias Phoenix.LiveView alias Phoenix.Socket.Broadcast @@ -388,4 +389,13 @@ defmodule BasenjiWeb.UserAuthTest do } end end + + def offset_user_token(token, amount_to_add, unit) do + dt = DateTime.add(DateTime.utc_now(:second), amount_to_add, unit) + + Basenji.Repo.update_all( + from(ut in UserToken, where: ut.token == ^token), + set: [inserted_at: dt, authenticated_at: dt] + ) + end end diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index 1051dfd..99455bf 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -17,8 +17,11 @@ defmodule BasenjiWeb.ConnCase do use ExUnit.CaseTemplate + import Ecto.Query + alias Basenji.Accounts.Scope alias Basenji.Accounts.User + alias Basenji.Accounts.UserToken using do quote do @@ -79,6 +82,11 @@ defmodule BasenjiWeb.ConnCase do defp maybe_set_token_authenticated_at(_token, nil), do: nil defp maybe_set_token_authenticated_at(token, authenticated_at) do - Basenji.AccountsFixtures.override_token_authenticated_at(token, authenticated_at) + Basenji.Repo.update_all( + from(t in UserToken, + where: t.token == ^token + ), + set: [authenticated_at: authenticated_at] + ) end end diff --git a/test/support/fixtures/accounts_fixtures.ex b/test/support/fixtures/accounts_fixtures.ex deleted file mode 100644 index b7d54e2..0000000 --- a/test/support/fixtures/accounts_fixtures.ex +++ /dev/null @@ -1,56 +0,0 @@ -defmodule Basenji.AccountsFixtures do - @moduledoc """ - This module defines test helpers for creating - entities via the `Basenji.Accounts` context. - """ - - import Ecto.Query - - alias Basenji.Accounts - # alias Basenji.Accounts.Scope - # alias Basenji.Accounts.User - - # def user_fixture(attrs \\ %{}) do - # user = %{} - # - # token = - # extract_user_token(fn url -> - # Accounts.deliver_login_instructions(user, url) - # end) - # - # {:ok, {user, _expired_tokens}} = - # Accounts.login_user_by_magic_link(token) - # - # user - # end - - def extract_user_token(fun) do - {:ok, captured_email} = fun.(&"[TOKEN]#{&1}[TOKEN]") - [_, token | _] = String.split(captured_email.text_body, "[TOKEN]") - token - end - - def override_token_authenticated_at(token, authenticated_at) when is_binary(token) do - Basenji.Repo.update_all( - from(t in Accounts.UserToken, - where: t.token == ^token - ), - set: [authenticated_at: authenticated_at] - ) - end - - def generate_user_magic_link_token(user) do - {encoded_token, user_token} = Accounts.UserToken.build_email_token(user, "login") - Basenji.Repo.insert!(user_token) - {encoded_token, user_token.token} - end - - def offset_user_token(token, amount_to_add, unit) do - dt = DateTime.add(DateTime.utc_now(:second), amount_to_add, unit) - - Basenji.Repo.update_all( - from(ut in Accounts.UserToken, where: ut.token == ^token), - set: [inserted_at: dt, authenticated_at: dt] - ) - end -end diff --git a/test/test_helper.exs b/test/test_helper.exs index 4d34078..9dcee33 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -4,6 +4,20 @@ ExUnit.start(timeout: :infinity) Ecto.Adapters.SQL.Sandbox.mode(Basenji.Repo, :manual) defmodule TestHelper do + alias Basenji.Accounts.UserToken + + def extract_user_token(fun) do + {:ok, captured_email} = fun.(&"[TOKEN]#{&1}[TOKEN]") + [_, token | _] = String.split(captured_email.text_body, "[TOKEN]") + token + end + + def generate_user_magic_link_token(user) do + {encoded_token, user_token} = UserToken.build_email_token(user, "login") + Basenji.Repo.insert!(user_token) + {encoded_token, user_token.token} + end + def get_tmp_dir, do: Path.join(System.tmp_dir!(), "basenji") def drain_queue(queue, start_opts \\ [], drain_opts \\ []) From dd64317a770a6c346798806ac38acbd1c1835f7b Mon Sep 17 00:00:00 2001 From: Cam Cook Date: Sat, 16 Aug 2025 17:58:03 -0400 Subject: [PATCH 4/4] test(api): stashing --- lib/basenji/accounts.ex | 2 +- lib/basenji/accounts/user.ex | 7 +++ lib/basenji_web/live/accounts/login_live.ex | 58 +------------------ .../live/accounts/registration_live.ex | 25 +++++--- 4 files changed, 25 insertions(+), 67 deletions(-) diff --git a/lib/basenji/accounts.ex b/lib/basenji/accounts.ex index 5f0aa42..235abf7 100644 --- a/lib/basenji/accounts.ex +++ b/lib/basenji/accounts.ex @@ -14,7 +14,7 @@ defmodule Basenji.Accounts do def register_user(attrs) do %User{} - |> User.email_changeset(attrs) + |> User.changeset(attrs) |> Repo.insert() end diff --git a/lib/basenji/accounts/user.ex b/lib/basenji/accounts/user.ex index de4a17f..421da76 100644 --- a/lib/basenji/accounts/user.ex +++ b/lib/basenji/accounts/user.ex @@ -16,6 +16,13 @@ defmodule Basenji.Accounts.User do timestamps(type: :utc_datetime) end + def changeset(user, attrs \\ %{}, opts \\ []) do + user + |> cast(attrs, [:email, :password, :confirmed_at, :hashed_password]) + |> validate_email(opts) + |> validate_password(opts) + end + @doc """ A user changeset for registering or changing the email. diff --git a/lib/basenji_web/live/accounts/login_live.ex b/lib/basenji_web/live/accounts/login_live.ex index 01fb032..40ed78e 100644 --- a/lib/basenji_web/live/accounts/login_live.ex +++ b/lib/basenji_web/live/accounts/login_live.ex @@ -12,23 +12,6 @@ defmodule BasenjiWeb.Accounts.LoginLive do def handle_event("submit_password", _params, socket), do: {:noreply, assign(socket, :trigger_submit, true)} - def handle_event("submit_magic", %{"user" => %{"email" => email}}, socket) do - with [user] <- Accounts.list_users(email: email) do - Accounts.deliver_login_instructions( - user, - &url(~p"/users/log-in/#{&1}") - ) - end - - info = - "If your email is in our system, you will receive instructions for logging in shortly." - - socket - |> put_flash(:info, info) - |> push_navigate(to: ~p"/users/log-in") - |> then(&{:noreply, &1}) - end - defp assign_form(socket) do email = Phoenix.Flash.get(socket.assigns.flash, :email) || @@ -38,8 +21,6 @@ defmodule BasenjiWeb.Accounts.LoginLive do assign(socket, form: form, trigger_submit: false) end - defp local_mail_adapter?, do: Application.get_env(:basenji, Basenji.Mailer)[:adapter] == Local - def render(assigns) do ~H"""
@@ -59,40 +40,6 @@ defmodule BasenjiWeb.Accounts.LoginLive do
- -
- <.icon name="hero-information-circle" class="size-6 shrink-0" /> -
-

You are running the local mail adapter.

-

- To see sent emails, visit <.link href="/dev/mailbox" class="underline">the mailbox page. -

-
-
- - <.form - :let={f} - for={@form} - id="login_form_magic" - action={~p"/users/log-in"} - phx-submit="submit_magic" - > - <.input - readonly={!!@current_scope} - field={f[:email]} - type="email" - label="Email" - autocomplete="username" - required - phx-mounted={JS.focus()} - /> - <.button class="btn btn-primary w-full"> - Log in with email - - - -
or
- <.form :let={f} for={@form} @@ -116,10 +63,7 @@ defmodule BasenjiWeb.Accounts.LoginLive do autocomplete="current-password" /> <.button class="btn btn-primary w-full" name={@form[:remember_me].name} value="true"> - Log in and stay logged in - - <.button class="btn btn-primary btn-soft w-full mt-2"> - Log in only this time + Log in diff --git a/lib/basenji_web/live/accounts/registration_live.ex b/lib/basenji_web/live/accounts/registration_live.ex index 6a5de7e..680f686 100644 --- a/lib/basenji_web/live/accounts/registration_live.ex +++ b/lib/basenji_web/live/accounts/registration_live.ex @@ -12,17 +12,15 @@ defmodule BasenjiWeb.Accounts.RegistrationLive do end def handle_event("save", %{"user" => user_params}, socket) do + user_params = Map.put(user_params, "confirmed_at", DateTime.utc_now()) + case Accounts.register_user(user_params) do {:ok, user} -> - with {:ok, _} <- Accounts.deliver_login_instructions(user, &url(~p"/users/log-in/#{&1}")) do - socket - |> put_flash( - :info, - "An email was sent to #{user.email}, please access it to confirm your account." - ) - |> push_navigate(to: ~p"/users/log-in") - |> then(&{:noreply, &1}) - end + IO.inspect(user) + + socket + |> push_navigate(to: ~p"/users/log-in") + |> then(&{:noreply, &1}) {:error, %Ecto.Changeset{} = changeset} -> {:noreply, assign_form(socket, changeset)} @@ -64,6 +62,15 @@ defmodule BasenjiWeb.Accounts.RegistrationLive do phx-mounted={JS.focus()} /> + <.input + field={@form[:password]} + type="password" + label="Password" + autocomplete="password" + required + phx-mounted={JS.focus()} + /> + <.button phx-disable-with="Creating account..." class="btn btn-primary w-full"> Create an account