Issue/confusion in authenticated user Integration Tests while creating example app 'Rumbl' following "Programming Phoenix" flow

I’ve been working with Elixir for some time now, not much experienced with Plug/Phoenix internals. Recently I’ve been following “Programming Phoenix” book’s flow for creating “Rumbl” project in it.

I did took some different decision along the way, but they are on same tangent & design pattern… just differ in function definition to make me understand it better.

While writing Integration Tests for Video feature for Authenticated Users; faced an issue and managed to fix it but don’t clearly understand what caused it or fixed.

For example, initially I wrote a test as

+   1   describe "logged-in user for unauthorized video" do                           
+   2     setup %{conn: conn, login_as: username}, do: login(conn, username)          
+   3                                                                                 
+   4     @tag login_as: "alice"                                                      
+   5     test "does not access other user's video", %{conn: conn, user: user} do     
+   6       user_bob = user_fixture(%{username: "bob"})                               
+   7       attrs = Map.put(@create_attrs, :user_id, user_bob.id)                     
+   8       video = video_fixture(%{ attrs | title: "Bob" })                          
+   9                                                                                 
+  10       conn = get(conn, Routes.video_path(conn, :index))                         
+  11       refute html_response(conn, 200) =~ "Bob"                                  
+  12                                                                                 
+  13       assert_error_sent :not_found, fn ->                                       
+  14         get(conn, Routes.video_path(conn, :show, video))                        
+  15       end                                                                       
+  16                                                                                 
+  17       assert_error_sent :not_found, fn ->                                       
+  18         get(conn, Routes.video_path(conn, :edit, video))                        
+  19       end                                                                       
+  20                                                                                 
+  21       assert_error_sent :not_found, fn ->                                       
+  22         put(conn, Routes.video_path(conn, :update, video, video: @create_attrs))
+  23       end                                                                       
+  24                                                                                 
+  25       assert_error_sent :not_found, fn ->                                       
+  26         delete(conn, Routes.video_path(conn, :delete, video))                   
+  27       end                                                                       
+  28     end                                                                         
+  29   end  

Which failed for line 13 for redirect 302 happening instead of expected :not_found.

When I change it as following adding a re-login before :show; it passes.

+   1   describe "logged-in user for unauthorized video" do                           
+   2     setup %{conn: conn, login_as: username}, do: login(conn, username)          
+   3                                                                                 
+   4     @tag login_as: "alice"                                                      
+   5     test "does not access other user's video", %{conn: conn, user: user} do     
+   6       user_bob = user_fixture(%{username: "bob"})                               
+   7       attrs = Map.put(@create_attrs, :user_id, user_bob.id)                     
+   8       video = video_fixture(%{ attrs | title: "Bob" })                          
+   9                                                                                 
+  10       conn = get(conn, Routes.video_path(conn, :index))                         
+  11       refute html_response(conn, 200) =~ "Bob"                                  
+  12                                                                                 
++  13       {:ok, conn} = recycle(conn) |> conn_assign(user)                          
+  14       assert_error_sent :not_found, fn ->                                       
+  15         get(conn, Routes.video_path(conn, :show, video))                        
+  16       end                                                                       
+  17                                                                                 
+  18       assert_error_sent :not_found, fn ->                                       
+  19         get(conn, Routes.video_path(conn, :edit, video))                        
+  20       end                                                                       
+  21                                                                                 
+  22       assert_error_sent :not_found, fn ->                                       
+  23         put(conn, Routes.video_path(conn, :update, video, video: @create_attrs))
+  24       end                                                                       
+  25                                                                                 
+  26       assert_error_sent :not_found, fn ->                                       
+  27         delete(conn, Routes.video_path(conn, :delete, video))                   
+  28       end                                                                       
+  29     end                                                                         
+  30   end  

Could someone please help me understand.

Thanks :pray:

Append.Edit#1

My dependency version are

  defp deps do
    [
      {:phoenix, "~> 1.5.9"},
      {:phoenix_ecto, "~> 4.1"},
      {:ecto_sql, "~> 3.4"},
      {:postgrex, ">= 0.0.0"},
      {:phoenix_html, "~> 2.11"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:phoenix_live_dashboard, "~> 0.4"},
      {:telemetry_metrics, "~> 0.4"},
      {:telemetry_poller, "~> 0.4"},
      {:gettext, "~> 0.11"},
      {:jason, "~> 1.0"},
      {:plug_cowboy, "~> 2.0"},

      {:pbkdf2_elixir, "~> 1.4"}
    ]
  end

The behavior you’re describing sounds like the user isn’t getting set in the response cookies; can you post the source of the helpers you’re using (login, conn_assign) so folks can see them, or post the project someplace public?

I think it’s because of

conn = get(conn, Routes.video_path(conn, :index))

Where you re-assign the conn. Therefore when you reference conn in the later tests, it is not fresh, but still contains the result of this GET.

What if you try

new_conn = get(conn, Routes.video_path(conn, :index)

so the original conn will still be available for the following tests?

1 Like

the referenced code alongwith entire Sample Project is here at Github

respective controller code for this test is here

this does work… would you know why the CONN still works just resets the ASSIGNS

login_as only sets the user’s ID in conn.assigns, which do not persist between requests because recycle does not copy them:

Comparing the code in your project to the code from the book matches @APB9785’s diagnosis; the test in the book (video_controller_test.change4.exs) sets up conn and then uses it repeatedly without rebinding it.

1 Like
  • tests in the book does what @APB9785 suggests, and doesn’t re-assign

  • recycle does empty Assigns; but I only did that to clean re-assign after my tests were already failing for not using a separate new_conn for initial HTTP request