web/models/role_checker.ex
and fill it with the following code: defmodule Pxblog.RoleChecker do alias Pxblog.Repo alias Pxblog.Role def is_admin?(user) do (role = Repo.get(Role, user.role_id)) && role.admin end end
test/models/role_checker_test.exs
: defmodule Pxblog.RoleCheckerTest do use Pxblog.ModelCase alias Pxblog.TestHelper alias Pxblog.RoleChecker test "is_admin? is true when user has an admin role" do {:ok, role} = TestHelper.create_role(%{name: "Admin", admin: true}) {:ok, user} = TestHelper.create_user(role, %{email: "test@test.com", username: "user", password: "test", password_confirmation: "test"}) assert RoleChecker.is_admin?(user) end test "is_admin? is false when user does not have an admin role" do {:ok, role} = TestHelper.create_role(%{name: "User", admin: false}) {:ok, user} = TestHelper.create_user(role, %{email: "test@test.com", username: "user", password: "test", password_confirmation: "test"}) refute RoleChecker.is_admin?(user) end end
is_admin?
returns true
for the first and false
for the second. So how is the is_admin?
function is_admin?
from the RoleChecker
module requires a user, we can write a very simple test to check the performance. It turns out the code in which we can be sure! We run the tests and make sure they stay green.UserController
, so now is the time to connect the authorize_user
. Let's quickly plan what we will do now. We will allow users to edit, update and delete their own profiles, but only administrators can add new users.scrub_params
line in the web/controllers/user_controller.ex
add the following: plug :authorize_admin when action in [:new, :create] plug :authorize_user when action in [:edit, :update, :delete]
defp authorize_user(conn, _) do user = get_session(conn, :current_user) if user && (Integer.to_string(user.id) == conn.params["id"] || Pxblog.RoleChecker.is_admin?(user)) do conn else conn |> put_flash(:error, "You are not authorized to modify that user!") |> redirect(to: page_path(conn, :index)) |> halt() end end defp authorize_admin(conn, _) do user = get_session(conn, :current_user) if user && Pxblog.RoleChecker.is_admin?(user) do conn else conn |> put_flash(:error, "You are not authorized to create new users!") |> redirect(to: page_path(conn, :index)) |> halt() end end
authorize_user
call is essentially identical to what we had in PostController
, except for checking RoleChecker.is_admin?
.authorize_admin
function is even simpler. We only verify that the current user is an administrator.test/controllers/user_controller_test.exs
back to the file test/controllers/user_controller_test.exs
and change our tests so that they take into account the new conditions. setup do {:ok, user_role} = TestHelper.create_role(%{name: "user", admin: false}) {:ok, nonadmin_user} = TestHelper.create_user(user_role, %{email: "nonadmin@test.com", username: "nonadmin", password: "test", password_confirmation: "test"}) {:ok, admin_role} = TestHelper.create_role(%{name: "admin", admin: true}) {:ok, admin_user} = TestHelper.create_user(admin_role, %{email: "admin@test.com", username: "admin", password: "test", password_confirmation: "test"}) {:ok, conn: build_conn(), admin_role: admin_role, user_role: user_role, nonadmin_user: nonadmin_user, admin_user: admin_user} end
login_user
function from PostController
. defp login_user(conn, user) do post conn, session_path(conn, :create), user: %{username: user.username, password: user.password} end
index
action, so we can skip this test. The next test, the "renders form for new resources" (representing the action of new
), the restriction is imposed. The user must have administrator rights. @tag admin: true test "renders form for new resources", %{conn: conn, admin_user: admin_user} do conn = conn |> login_user(admin_user) |> get(user_path(conn, :new)) assert html_response(conn, 200) =~ "New user" end
@tag admin: true
over this test to mark it as an admin. Thus, we can run only similar tests instead of the entire set. Let's try: mix test --only admin
1) test renders form for new resources (Pxblog.UserControllerTest) test/controllers/user_controller_test.exs:26 ** (KeyError) key :role_id not found in: %{id: 348, username: “admin”} stacktrace: (pxblog) web/models/role_checker.ex:6: Pxblog.RoleChecker.is_admin?/1 (pxblog) web/controllers/user_controller.ex:84: Pxblog.UserController.authorize_admin/2 (pxblog) web/controllers/user_controller.ex:1: Pxblog.UserController.phoenix_controller_pipeline/2 (pxblog) lib/phoenix/router.ex:255: Pxblog.Router.dispatch/2 (pxblog) web/router.ex:1: Pxblog.Router.do_call/2 (pxblog) lib/pxblog/endpoint.ex:1: Pxblog.Endpoint.phoenix_pipeline/1 (pxblog) lib/phoenix/endpoint/render_errors.ex:34: Pxblog.Endpoint.call/2 (phoenix) lib/phoenix/test/conn_test.ex:193: Phoenix.ConnTest.dispatch/5 test/controllers/user_controller_test.exs:28
RoleChecker.is_admin?
function RoleChecker.is_admin?
. And we transfer a small subset of data received by the current_user
function from the sign_in
function of the sign_in
module.role_id
to them as well. I made changes to the web/controllers/session_controller.ex
file as shown below: defp sign_in(user, password, conn) do if checkpw(password, user.password_digest) do conn |> put_session(:current_user, %{id: user.id, username: user.username, role_id: user.role_id}) |> put_flash(:info, "Sign in successful!") |> redirect(to: page_path(conn, :index)) else failed_login(conn) end end
admin
tag. $ mix test --only admin
UserController
controller. Go back to the file test/controllers/user_controller_test.exs
: @tag admin: true test "redirects from new form when not admin", %{conn: conn, nonadmin_user: nonadmin_user} do conn = login_user(conn, nonadmin_user) conn = get conn, user_path(conn, :new) assert get_flash(conn, :error) == "You are not authorized to create new users!" assert redirected_to(conn) == page_path(conn, :index) assert conn.halted end
create
action. Create one test for both cases. @tag admin: true test "creates resource and redirects when data is valid", %{conn: conn, user_role: user_role, admin_user: admin_user} do conn = login_user(conn, admin_user) conn = post conn, user_path(conn, :create), user: valid_create_attrs(user_role) assert redirected_to(conn) == user_path(conn, :index) assert Repo.get_by(User, @valid_attrs) end @tag admin: true test "redirects from creating user when not admin", %{conn: conn, user_role: user_role, nonadmin_user: nonadmin_user} do conn = login_user(conn, nonadmin_user) conn = post conn, user_path(conn, :create), user: valid_create_attrs(user_role) assert get_flash(conn, :error) == "You are not authorized to create new users!" assert redirected_to(conn) == page_path(conn, :index) assert conn.halted end @tag admin: true test "does not create resource and renders errors when data is invalid", %{conn: conn, admin_user: admin_user} do conn = login_user(conn, admin_user) conn = post conn, user_path(conn, :create), user: @invalid_attrs assert html_response(conn, 200) =~ "New user" end
show
action, because we have not added any new conditions to it. We will act on the same template until the user_controller_test.exs
file looks like: defmodule Pxblog.UserControllerTest do use Pxblog.ConnCase alias Pxblog.User alias Pxblog.TestHelper @valid_create_attrs %{email: "test@test.com", username: "test", password: "test", password_confirmation: "test"} @valid_attrs %{email: "test@test.com", username: "test"} @invalid_attrs %{} setup do {:ok, user_role} = TestHelper.create_role(%{name: "user", admin: false}) {:ok, nonadmin_user} = TestHelper.create_user(user_role, %{email: "nonadmin@test.com", username: "nonadmin", password: "test", password_confirmation: "test"}) {:ok, admin_role} = TestHelper.create_role(%{name: "admin", admin: true}) {:ok, admin_user} = TestHelper.create_user(admin_role, %{email: "admin@test.com", username: "admin", password: "test", password_confirmation: "test"}) {:ok, conn: build_conn(), admin_role: admin_role, user_role: user_role, nonadmin_user: nonadmin_user, admin_user: admin_user} end defp valid_create_attrs(role) do Map.put(@valid_create_attrs, :role_id, role.id) end defp login_user(conn, user) do post conn, session_path(conn, :create), user: %{username: user.username, password: user.password} end test "lists all entries on index", %{conn: conn} do conn = get conn, user_path(conn, :index) assert html_response(conn, 200) =~ "Listing users" end @tag admin: true test "renders form for new resources", %{conn: conn, admin_user: admin_user} do conn = login_user(conn, admin_user) conn = get conn, user_path(conn, :new) assert html_response(conn, 200) =~ "New user" end @tag admin: true test "redirects from new form when not admin", %{conn: conn, nonadmin_user: nonadmin_user} do conn = login_user(conn, nonadmin_user) conn = get conn, user_path(conn, :new) assert get_flash(conn, :error) == "You are not authorized to create new users!" assert redirected_to(conn) == page_path(conn, :index) assert conn.halted end @tag admin: true test "creates resource and redirects when data is valid", %{conn: conn, user_role: user_role, admin_user: admin_user} do conn = login_user(conn, admin_user) conn = post conn, user_path(conn, :create), user: valid_create_attrs(user_role) assert redirected_to(conn) == user_path(conn, :index) assert Repo.get_by(User, @valid_attrs) end @tag admin: true test "redirects from creating user when not admin", %{conn: conn, user_role: user_role, nonadmin_user: nonadmin_user} do conn = login_user(conn, nonadmin_user) conn = post conn, user_path(conn, :create), user: valid_create_attrs(user_role) assert get_flash(conn, :error) == "You are not authorized to create new users!" assert redirected_to(conn) == page_path(conn, :index) assert conn.halted end @tag admin: true test "does not create resource and renders errors when data is invalid", %{conn: conn, admin_user: admin_user} do conn = login_user(conn, admin_user) conn = post conn, user_path(conn, :create), user: @invalid_attrs assert html_response(conn, 200) =~ "New user" end test "shows chosen resource", %{conn: conn} do user = Repo.insert! %User{} conn = get conn, user_path(conn, :show, user) assert html_response(conn, 200) =~ "Show user" end test "renders page not found when id is nonexistent", %{conn: conn} do assert_error_sent 404, fn -> get conn, user_path(conn, :show, -1) end end @tag admin: true test "renders form for editing chosen resource when logged in as that user", %{conn: conn, nonadmin_user: nonadmin_user} do conn = login_user(conn, nonadmin_user) conn = get conn, user_path(conn, :edit, nonadmin_user) assert html_response(conn, 200) =~ "Edit user" end @tag admin: true test "renders form for editing chosen resource when logged in as an admin", %{conn: conn, admin_user: admin_user, nonadmin_user: nonadmin_user} do conn = login_user(conn, admin_user) conn = get conn, user_path(conn, :edit, nonadmin_user) assert html_response(conn, 200) =~ "Edit user" end @tag admin: true test "redirects away from editing when logged in as a different user", %{conn: conn, nonadmin_user: nonadmin_user, admin_user: admin_user} do conn = login_user(conn, nonadmin_user) conn = get conn, user_path(conn, :edit, admin_user) assert get_flash(conn, :error) == "You are not authorized to modify that user!" assert redirected_to(conn) == page_path(conn, :index) assert conn.halted end @tag admin: true test "updates chosen resource and redirects when data is valid when logged in as that user", %{conn: conn, nonadmin_user: nonadmin_user} do conn = login_user(conn, nonadmin_user) conn = put conn, user_path(conn, :update, nonadmin_user), user: @valid_create_attrs assert redirected_to(conn) == user_path(conn, :show, nonadmin_user) assert Repo.get_by(User, @valid_attrs) end @tag admin: true test "updates chosen resource and redirects when data is valid when logged in as an admin", %{conn: conn, admin_user: admin_user} do conn = login_user(conn, admin_user) conn = put conn, user_path(conn, :update, admin_user), user: @valid_create_attrs assert redirected_to(conn) == user_path(conn, :show, admin_user) assert Repo.get_by(User, @valid_attrs) end @tag admin: true test "does not update chosen resource when logged in as different user", %{conn: conn, nonadmin_user: nonadmin_user, admin_user: admin_user} do conn = login_user(conn, nonadmin_user) conn = put conn, user_path(conn, :update, admin_user), user: @valid_create_attrs assert get_flash(conn, :error) == "You are not authorized to modify that user!" assert redirected_to(conn) == page_path(conn, :index) assert conn.halted end @tag admin: true test "does not update chosen resource and renders errors when data is invalid", %{conn: conn, nonadmin_user: nonadmin_user} do conn = login_user(conn, nonadmin_user) conn = put conn, user_path(conn, :update, nonadmin_user), user: @invalid_attrs assert html_response(conn, 200) =~ "Edit user" end @tag admin: true test "deletes chosen resource when logged in as that user", %{conn: conn, user_role: user_role} do {:ok, user} = TestHelper.create_user(user_role, @valid_create_attrs) conn = login_user(conn, user) |> delete(user_path(conn, :delete, user)) assert redirected_to(conn) == user_path(conn, :index) refute Repo.get(User, user.id) end @tag admin: true test "deletes chosen resource when logged in as an admin", %{conn: conn, user_role: user_role, admin_user: admin_user} do {:ok, user} = TestHelper.create_user(user_role, @valid_create_attrs) conn = login_user(conn, admin_user) |> delete(user_path(conn, :delete, user)) assert redirected_to(conn) == user_path(conn, :index) refute Repo.get(User, user.id) end @tag admin: true test "redirects away from deleting chosen resource when logged in as a different user", %{conn: conn, user_role: user_role, nonadmin_user: nonadmin_user} do {:ok, user} = TestHelper.create_user(user_role, @valid_create_attrs) conn = login_user(conn, nonadmin_user) |> delete(user_path(conn, :delete, user)) assert get_flash(conn, :error) == "You are not authorized to modify that user!" assert redirected_to(conn) == page_path(conn, :index) assert conn.halted end end
web/controllers/post_controller.ex
and change the authorize_user
function to use the RoleChecker.is_admin?
auxiliary function RoleChecker.is_admin?
. If the user is an administrator, then we will give him full control over the change of user posts. defp authorize_user(conn, _) do user = get_session(conn, :current_user) if user && (Integer.to_string(user.id) == conn.params["user_id"] || Pxblog.RoleChecker.is_admin?(user)) do conn else conn |> put_flash(:error, "You are not authorized to modify that post!") |> redirect(to: page_path(conn, :index)) |> halt() end end
test/controllers/post_controller_test.exs
open the file test/controllers/post_controller_test.exs
and add a few more tests to cover the authorization rules: test "redirects when trying to delete a post for a different user", %{conn: conn, role: role, post: post} do {:ok, other_user} = TestHelper.create_user(role, %{email: "test2@test.com", username: "test2", password: "test", password_confirmation: "test"}) conn = delete conn, user_post_path(conn, :delete, other_user, post) assert get_flash(conn, :error) == "You are not authorized to modify that post!" assert redirected_to(conn) == page_path(conn, :index) assert conn.halted end test "renders form for editing chosen resource when logged in as admin", %{conn: conn, user: user, post: post} do {:ok, role} = TestHelper.create_role(%{name: "Admin", admin: true}) {:ok, admin} = TestHelper.create_user(role, %{username: "admin", email: "admin@test.com", password: "test", password_confirmation: "test"}) conn = login_user(conn, admin) |> get(user_post_path(conn, :edit, user, post)) assert html_response(conn, 200) =~ "Edit post" end test "updates chosen resource and redirects when data is valid when logged in as admin", %{conn: conn, user: user, post: post} do {:ok, role} = TestHelper.create_role(%{name: "Admin", admin: true}) {:ok, admin} = TestHelper.create_user(role, %{username: "admin", email: "admin@test.com", password: "test", password_confirmation: "test"}) conn = login_user(conn, admin) |> put(user_post_path(conn, :update, user, post), post: @valid_attrs) assert redirected_to(conn) == user_post_path(conn, :show, user, post) assert Repo.get_by(Post, @valid_attrs) end test "does not update chosen resource and renders errors when data is invalid when logged in as admin", %{conn: conn, user: user, post: post} do {:ok, role} = TestHelper.create_role(%{name: "Admin", admin: true}) {:ok, admin} = TestHelper.create_user(role, %{username: "admin", email: "admin@test.com", password: "test", password_confirmation: "test"}) conn = login_user(conn, admin) |> put(user_post_path(conn, :update, user, post), post: %{"body" => nil}) assert html_response(conn, 200) =~ "Edit post" end test "deletes chosen resource when logged in as admin", %{conn: conn, user: user, post: post} do {:ok, role} = TestHelper.create_role(%{name: "Admin", admin: true}) {:ok, admin} = TestHelper.create_user(role, %{username: "admin", email: "admin@test.com", password: "test", password_confirmation: "test"}) conn = login_user(conn, admin) |> delete(user_post_path(conn, :delete, user, post)) assert redirected_to(conn) == user_post_path(conn, :index, user) refute Repo.get(Post, post.id) end
part_3
branch part_3
attempts to create a new user will result in an error due to the lack of a role (since we made the presence of role_id
when creating the user). Let's first examine the problem, and only then begin to fix it. When we log in as an administrator, go to the address /users/new
, fill in all the fields and click on the button, we get the following error:new, create, edit update
. Add alias Pxblog.Role
top of UserController ( web/controllers/user_controller.ex
) if this is not yet there. Then we will make changes to all previously listed actions: def new(conn, _params) do roles = Repo.all(Role) changeset = User.changeset(%User{}) render(conn, "new.html", changeset: changeset, roles: roles) end def edit(conn, %{"id" => id}) do roles = Repo.all(Role) user = Repo.get!(User, id) changeset = User.changeset(user) render(conn, "edit.html", user: user, changeset: changeset, roles: roles) end def create(conn, %{"user" => user_params}) do roles = Repo.all(Role) changeset = User.changeset(%User{}, user_params) case Repo.insert(changeset) do {:ok, _user} -> conn |> put_flash(:info, "User created successfully.") |> redirect(to: user_path(conn, :index)) {:error, changeset} -> render(conn, "new.html", changeset: changeset, roles: roles) end end def update(conn, %{"id" => id, "user" => user_params}) do roles = Repo.all(Role) user = Repo.get!(User, id) changeset = User.changeset(user, user_params) case Repo.update(changeset) do {:ok, user} -> conn |> put_flash(:info, "User updated successfully.") |> redirect(to: user_path(conn, :show, user)) {:error, changeset} -> render(conn, "edit.html", user: user, changeset: changeset, roles: roles) end end
Repo.all(Role)
and added them to the assigns
list, which we pass to the view (including in case of an error).Phoenix.Html
. So let's see how this is done in the documentation : select(form, field, values, opts \\ []) Generates a select tag with the given values.
values
either a regular list (in the format of [value, value, value]
) or a list of keywords (in the format of [displayed: value, displayed: value]
). In our case, we need to display the names of the roles and at the same time pass the value of the identifier of the selected role when sending the form. We cannot just blindly throw the @roles
variable into an auxiliary function, because it does not fit into any of the above formats. So let's write a function in View
that will simplify our task. defmodule Pxblog.UserView do use Pxblog.Web, :view def roles_for_select(roles) do roles |> Enum.map(&["#{&1.name}": &1.id]) |> List.flatten end end
roles_for_select
function, which simply accepts a collection of roles. Let's look at line by line what this function does. Let's start with the collection of roles, which we pass to the following function along the chain: Enum.map(&["#{&1.name}": &1.id])
&/&1
is an abbreviation for anonymous functions, which can be rewritten in its full version as follows: Enum.map(roles, fn role -> ["#{role.name}": role.id] end)
map
operation to return a list of smaller key lists, where the name of the role is the key and the identifier of the role is the value. roles = [%Role{name: "Admin Role", id: 1}, %Role{name: "User Role", id: 2}]
[["Admin Role": 1], ["User Role": 2]]
List.flatten
, which removes the extra nesting. So our final result: ["Admin Role": 1, "User Role": 2]
web/templates/user/new.html.eex
: <h2>New user</h2> <%= render "form.html", changeset: @changeset, action: user_path(@conn, :create), roles: @roles %> <%= link "Back", to: user_path(@conn, :index) %>
web/templates/user/edit.html.eex
: <h2>Edit user</h2> <%= render "form.html", changeset: @changeset, action: user_path(@conn, :update, @user), roles: @roles %> <%= link "Back", to: user_path(@conn, :index) %>
web/templates/user/form.html.eex
. As a result, a drop-down list will appear in the form, including all possible roles for the user to transfer. Add the following code to the Submit button: <div class="form-group"> <%= label f, :role_id, "Role", class: "control-label" %> <%= select f, :role_id, roles_for_select(@roles), class: "form-control" %> <%= error_tag f, :role_id %> </div>
find_or_create
: alias Pxblog.Repo alias Pxblog.Role alias Pxblog.User import Ecto.Query, only: [from: 2] find_or_create_role = fn role_name, admin -> case Repo.all(from r in Role, where: r.name == ^role_name and r.admin == ^admin) do [] -> %Role{} |> Role.changeset(%{name: role_name, admin: admin}) |> Repo.insert!() _ -> IO.puts "Role: #{role_name} already exists, skipping" end end find_or_create_user = fn username, email, role -> case Repo.all(from u in User, where: u.username == ^username and u.email == ^email) do [] -> %User{} |> User.changeset(%{username: username, email: email, password: "test", password_confirmation: "test", role_id: role.id}) |> Repo.insert!() _ -> IO.puts "User: #{username} already exists, skipping" end end _user_role = find_or_create_role.("User Role", false) admin_role = find_or_create_role.("Admin Role", true) _admin_user = find_or_create_user.("admin", "admin@test.com", admin_role)
Repo
, Role
and User
. We also import the from
function from
the Ecto.Query
module to use the convenient query syntax. Then take a look at the anonymous function find_or_create_role
. The function itself simply accepts the name of the role and the admin flag as arguments.Repo.all
(note the ^ sign following each variable inside the where
clause, because we want to compare values instead of matching to the sample). And throw the result in the case statement. If Repo.all
did not find anything, we will get back an empty list, therefore, we need to add a role. Otherwise, we assume that the role already exists and proceed to load the rest of the file. The find_or_create_user
function does the same thing but uses different criteria.admin_role
name admin_role
with an underscore. Later, we may want to use user_role
or admin_user
for further use in the initial data file, but for the time being we will leave this code alone by referring to the underscore. This will make the raw data file look tidy and clean. Now everything is ready to load the initial data: $ mix run priv/repo/seeds.exs [debug] SELECT r0.”id”, r0.”name”, r0.”admin”, r0.”inserted_at”, r0.”updated_at” FROM “roles” AS r0 WHERE ((r0.”name” = $1) AND (r0.”admin” = $2)) [“User Role”, false] OK query=81.7ms queue=2.8ms [debug] BEGIN [] OK query=0.2ms [debug] INSERT INTO “roles” (“admin”, “inserted_at”, “name”, “updated_at”) VALUES ($1, $2, $3, $4) RETURNING “id” [false, {{2015, 11, 6}, {19, 35, 49, 0}}, “User Role”, {{2015, 11, 6}, {19, 35, 49, 0}}] OK query=0.8ms [debug] COMMIT [] OK query=0.4ms [debug] SELECT r0.”id”, r0.”name”, r0.”admin”, r0.”inserted_at”, r0.”updated_at” FROM “roles” AS r0 WHERE ((r0.”name” = $1) AND (r0.”admin” = $2)) [“Admin Role”, true] OK query=0.4ms [debug] BEGIN [] OK query=0.2ms [debug] INSERT INTO “roles” (“admin”, “inserted_at”, “name”, “updated_at”) VALUES ($1, $2, $3, $4) RETURNING “id” [true, {{2015, 11, 6}, {19, 35, 49, 0}}, “Admin Role”, {{2015, 11, 6}, {19, 35, 49, 0}}] OK query=0.4ms [debug] COMMIT [] OK query=0.3ms [debug] SELECT u0.”id”, u0.”username”, u0.”email”, u0.”password_digest”, u0.”role_id”, u0.”inserted_at”, u0.”updated_at” FROM “users” AS u0 WHERE ((u0.”username” = $1) AND (u0.”email” = $2)) [“admin”, “admin@test.com”] OK query=0.7ms [debug] BEGIN [] OK query=0.3ms [debug] INSERT INTO “users” (“email”, “inserted_at”, “password_digest”, “role_id”, “updated_at”, “username”) VALUES ($1, $2, $3, $4, $5, $6) RETURNING “id” [“admin@test.com”, {{2015, 11, 6}, {19, 35, 49, 0}}, “$2b$12$.MuPBUVe/7/9HSOsccJYUOAD5IKEB77Pgz2oTJ/UvTvWYwAGn/Li”, 2, {{2015, 11, 6}, {19, 35, 49, 0}}, “admin”] OK query=1.2ms [debug] COMMIT [] OK query=1.1ms
INSERT
. Awesome! To be completely sure that everything works as it should, let's try to load them again and make sure that there are no insert operations: $ mix run priv/repo/seeds.exs Role: User Role already exists, skipping [debug] SELECT r0.”id”, r0.”name”, r0.”admin”, r0.”inserted_at”, r0.”updated_at” FROM “roles” AS r0 WHERE ((r0.”name” = $1) AND (r0.”admin” = $2)) [“User Role”, false] OK query=104.8ms queue=3.6ms Role: Admin Role already exists, skipping [debug] SELECT r0.”id”, r0.”name”, r0.”admin”, r0.”inserted_at”, r0.”updated_at” FROM “roles” AS r0 WHERE ((r0.”name” = $1) AND (r0.”admin” = $2)) [“Admin Role”, true] OK query=0.6ms User: admin already exists, skipping [debug] SELECT u0.”id”, u0.”username”, u0.”email”, u0.”password_digest”, u0.”role_id”, u0.”inserted_at”, u0.”updated_at” FROM “users” AS u0 WHERE ((u0.”username” = $1) AND (u0.”email” = $2)) [“admin”, “admin@test.com”] OK query=0.8ms
Ecto
!test/support/test_helper.ex
and change the function create_user
: def create_user(role, %{email: email, username: username, password: password, password_confirmation: password_confirmation}) do if user = Repo.get_by(User, username: username) do Repo.delete(user) end role |> build_assoc(:users) |> User.changeset(%{email: email, username: username, password: password, password_confirmation: password_confirmation}) |> Repo.insert end
Source: https://habr.com/ru/post/316368/
All Articles