auto-confirm admin during setup, skip email verification
Some checks failed
deploy / deploy (push) Has been cancelled
Some checks failed
deploy / deploy (push) Has been cancelled
Setup wizard no longer requires email delivery. Admin account is auto-confirmed and auto-logged-in via token redirect. Adds setup secret gate for prod (logged on boot), SMTP env var config in runtime.exs, email_configured? helper, and admin warning banner when email isn't set up. Includes plan files for this task and the follow-up email settings UI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -64,44 +64,34 @@ defmodule Berrypod.AccountsTest do
|
||||
end
|
||||
end
|
||||
|
||||
describe "admin_email/0" do
|
||||
test "returns nil when no users exist" do
|
||||
assert is_nil(Accounts.admin_email())
|
||||
describe "register_and_confirm_admin/1" do
|
||||
test "creates a confirmed admin user" do
|
||||
email = unique_user_email()
|
||||
assert {:ok, user} = Accounts.register_and_confirm_admin(%{email: email})
|
||||
assert user.email == email
|
||||
assert user.confirmed_at
|
||||
end
|
||||
|
||||
test "returns the admin email" do
|
||||
user = user_fixture()
|
||||
assert Accounts.admin_email() == user.email
|
||||
test "fails if admin already exists" do
|
||||
user_fixture()
|
||||
|
||||
assert {:error, :admin_already_exists} =
|
||||
Accounts.register_and_confirm_admin(%{email: unique_user_email()})
|
||||
end
|
||||
|
||||
test "validates email" do
|
||||
assert {:error, changeset} = Accounts.register_and_confirm_admin(%{email: "bad"})
|
||||
assert errors_on(changeset).email
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_unconfirmed_admin/0" do
|
||||
test "returns nil when no users exist" do
|
||||
assert is_nil(Accounts.get_unconfirmed_admin())
|
||||
end
|
||||
|
||||
test "returns unconfirmed user" do
|
||||
user = unconfirmed_user_fixture()
|
||||
assert Accounts.get_unconfirmed_admin().id == user.id
|
||||
end
|
||||
|
||||
test "returns nil for confirmed user" do
|
||||
_user = user_fixture()
|
||||
assert is_nil(Accounts.get_unconfirmed_admin())
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete_unconfirmed_user/1" do
|
||||
test "deletes an unconfirmed user" do
|
||||
user = unconfirmed_user_fixture()
|
||||
assert {:ok, _} = Accounts.delete_unconfirmed_user(user)
|
||||
refute Accounts.has_admin?()
|
||||
end
|
||||
|
||||
test "refuses to delete a confirmed user" do
|
||||
describe "generate_login_token/1" do
|
||||
test "creates a login token for the user" do
|
||||
user = user_fixture()
|
||||
assert {:error, :already_confirmed} = Accounts.delete_unconfirmed_user(user)
|
||||
assert Accounts.has_admin?()
|
||||
token = Accounts.generate_login_token(user)
|
||||
assert is_binary(token)
|
||||
assert found_user = Accounts.get_user_by_magic_link_token(token)
|
||||
assert found_user.id == user.id
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
40
test/berrypod_web/controllers/setup_controller_test.exs
Normal file
40
test/berrypod_web/controllers/setup_controller_test.exs
Normal file
@@ -0,0 +1,40 @@
|
||||
defmodule BerrypodWeb.SetupControllerTest do
|
||||
use BerrypodWeb.ConnCase, async: true
|
||||
|
||||
import Berrypod.AccountsFixtures
|
||||
|
||||
alias Berrypod.Accounts
|
||||
|
||||
describe "GET /setup/login/:token" do
|
||||
test "logs in with a valid token and redirects to /setup", %{conn: conn} do
|
||||
user = user_fixture()
|
||||
token = Accounts.generate_login_token(user)
|
||||
|
||||
conn = get(conn, ~p"/setup/login/#{token}")
|
||||
|
||||
assert redirected_to(conn) == ~p"/setup"
|
||||
assert get_session(conn, :user_token)
|
||||
end
|
||||
|
||||
test "redirects to /setup with error for invalid token", %{conn: conn} do
|
||||
conn = get(conn, ~p"/setup/login/invalid-token")
|
||||
|
||||
assert redirected_to(conn) == ~p"/setup"
|
||||
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ "Login failed"
|
||||
end
|
||||
|
||||
test "token is consumed after use", %{conn: conn} do
|
||||
user = user_fixture()
|
||||
token = Accounts.generate_login_token(user)
|
||||
|
||||
# First use succeeds
|
||||
conn1 = get(conn, ~p"/setup/login/#{token}")
|
||||
assert redirected_to(conn1) == ~p"/setup"
|
||||
|
||||
# Second use fails (token consumed)
|
||||
conn2 = get(conn, ~p"/setup/login/#{token}")
|
||||
assert redirected_to(conn2) == ~p"/setup"
|
||||
assert Phoenix.Flash.get(conn2.assigns.flash, :error) =~ "Login failed"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4,7 +4,7 @@ defmodule BerrypodWeb.Setup.OnboardingTest do
|
||||
import Phoenix.LiveViewTest
|
||||
import Berrypod.AccountsFixtures
|
||||
|
||||
alias Berrypod.{Accounts, Products, Settings}
|
||||
alias Berrypod.{Products, Settings}
|
||||
|
||||
describe "access rules" do
|
||||
test "accessible on fresh install (no admin)", %{conn: conn} do
|
||||
@@ -30,12 +30,11 @@ defmodule BerrypodWeb.Setup.OnboardingTest do
|
||||
assert {:live_redirect, %{to: "/admin"}} = redirect
|
||||
end
|
||||
|
||||
test "shows check inbox when admin exists but not logged in", %{conn: conn} do
|
||||
_user = unconfirmed_user_fixture()
|
||||
test "redirects to login when admin exists but not logged in", %{conn: conn} do
|
||||
_user = user_fixture()
|
||||
|
||||
{:ok, _view, html} = live(conn, ~p"/setup")
|
||||
assert html =~ "Check your inbox"
|
||||
refute html =~ "Connect a print provider"
|
||||
{:error, redirect} = live(conn, ~p"/setup")
|
||||
assert {:live_redirect, %{to: "/users/log-in"}} = redirect
|
||||
end
|
||||
|
||||
test "redirects to / when site is already live", %{conn: conn} do
|
||||
@@ -57,44 +56,29 @@ defmodule BerrypodWeb.Setup.OnboardingTest do
|
||||
end
|
||||
end
|
||||
|
||||
describe "phase: email form" do
|
||||
test "shows only email form on fresh install", %{conn: conn} do
|
||||
describe "fresh install (no admin)" do
|
||||
test "shows all three cards with email form active", %{conn: conn} do
|
||||
{:ok, _view, html} = live(conn, ~p"/setup")
|
||||
|
||||
assert html =~ "Create admin account"
|
||||
refute html =~ "Connect a print provider"
|
||||
refute html =~ "Connect payments"
|
||||
assert html =~ "Connect a print provider"
|
||||
assert html =~ "Connect payments"
|
||||
end
|
||||
|
||||
test "creating account auto-confirms and redirects to login", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/setup")
|
||||
|
||||
view
|
||||
|> form(~s(form[phx-submit="create_account"]), account: %{email: "admin@example.com"})
|
||||
|> render_submit()
|
||||
|
||||
# The LiveView redirects to /setup/login/:token
|
||||
{path, _flash} = assert_redirect(view)
|
||||
assert path =~ ~r"/setup/login/.+"
|
||||
end
|
||||
end
|
||||
|
||||
describe "phase: check inbox" do
|
||||
test "shows check inbox after creating account", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/setup")
|
||||
|
||||
html =
|
||||
view
|
||||
|> form("form", account: %{email: "admin@example.com"})
|
||||
|> render_submit()
|
||||
|
||||
assert html =~ "Check your inbox"
|
||||
assert html =~ "admin@example.com"
|
||||
refute html =~ "Connect a print provider"
|
||||
end
|
||||
|
||||
test "start over resets to email form", %{conn: conn} do
|
||||
_user = unconfirmed_user_fixture()
|
||||
|
||||
{:ok, view, _html} = live(conn, ~p"/setup")
|
||||
|
||||
html = render_click(view, "start_over")
|
||||
|
||||
assert html =~ "Create admin account"
|
||||
refute html =~ "Check your inbox"
|
||||
refute Accounts.has_admin?()
|
||||
end
|
||||
end
|
||||
|
||||
describe "phase: configure (logged in)" do
|
||||
describe "configure (logged in)" do
|
||||
setup :register_and_log_in_user
|
||||
|
||||
test "shows provider and stripe steps", %{conn: conn, user: user} do
|
||||
|
||||
Reference in New Issue
Block a user