Testing connection assigns current_user

I have a very simple web application that currently only supports users registering/logging in and out etc.

I have followed the procedure from the Chris McCord book on how to do this.

I want to write unit tests to make my app future proof and all the other goodness tests provide.

I have written my controller in such a way that only a logged in user can edit his own profile. If the user is trying to edit a profile from another user the user is redirected to the homepage.

In my unit test I want to assign a user to the connection, and if that user’s id matches the id of the user that’s going to be edited, I allow it.

I observe that in my controller the assigns of the connection are always empty. So my question is, how can I assign the current user to my connection for this test?

I have added the relevant code below, and the entire repo is online available here: https://gitlab.call-cc.be/petprojects/gym/tree/user_front_test

Test

  defp create_user(_) do
    user = fixture(:user)
    {:ok, user: user}
  end

  def fixture(:user) do
    {:ok, user} = Gymbook.Domain.create_user(@valid_attrs)
    user
  end

  describe "edit user" do
    setup [:create_user]

    @tag :logged_in
    test "can edit own profile", %{conn: conn, user: user} do
      # Add user to connection.
      conn = Plug.Conn.assign(conn, :current_user, user)

      conn = get(conn, user_path(conn, :edit, user))

      assert html_response(conn, 200) =~ "Edit User"
    end
end

UserController

  def edit(conn, %{"id" => id}) do
    if current_user_id?(conn, id) do
      user = Domain.get_user!(id)
      changeset = Domain.change_user(user)
      render(conn, "edit.html", user: user, changeset: changeset)
    else
      handle_not_allowed(conn)
    end
  end

  defp current_user_id?(conn, id) do
    current_user = conn.assigns[:current_user]
    not (current_user == nil or "#{current_user.id}" != id)
  end

  defp handle_not_allowed(conn) do
    conn
    |> put_flash(:info, "Not allowed to do that. Sorry.")
    |> redirect(to: page_path(conn, :index))
  end

Gymbook.WebsiteWeb.Auth

defmodule Gymbook.WebsiteWeb.Auth do
  import Plug.Conn
  import Comeonin.Bcrypt, only: [checkpw: 2, dummy_checkpw: 0]

  def init(opts) do
    Keyword.fetch!(opts, :repo)
  end

  def call(conn, repo) do
    user_id = get_session(conn, :user_id)
    user = user_id && repo.get(Gymbook.Domain.User, user_id)
    assign(conn, :current_user, user)
  end

  def login(conn, user) do
    conn
    |> assign(:current_user, user)
    |> put_session(:user_id, user.id)
    |> configure_session(renew: true)
  end

  def logout(conn) do
    configure_session(conn, drop: true)
  end

  def login_by_username_and_pass(conn, username, given_pass, opts) do
    repo = Keyword.fetch!(opts, :repo)
    user = repo.get_by(Gymbook.Domain.User, username: username)

    cond do
      user && checkpw(given_pass, user.password_hash) ->
        {:ok, login(conn, user)}

      user ->
        {:error, :unauthorized, conn}

      true ->
        dummy_checkpw()
        {:error, :not_found, conn}
    end
  end
end

You assign the current user only to the conn, which is used to generate the url. It’s not needed there, but the conn you supply to get() needs it. I’d suggest you to not do such manipulation inline, but in a separate line like conn = Plug.Conn.assign(conn, :current_user, user).

I see. I did not notice that. I did indeed manipulate the connection before both calls, and that does not work either. I will update the post to reflect this.