Skip to content

Tests crashing related to render_upload #4079

@quinnmccourt

Description

@quinnmccourt

Environment

  • Elixir version (please paste the output of elixir -v):
Erlang/OTP 27 [erts-15.2.7] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit]

Elixir 1.19.4 (compiled with Erlang/OTP 27)
  • Phoenix and LiveView versions (mix deps | grep -w 'phoenix\|phoenix_live_view'):
* phoenix 1.8.3 (Hex package) (mix)
  locked at 1.8.3 (phoenix) 36169f95
* phoenix_live_view 1.1.18 (Hex package) (mix)
  locked at 1.1.18 (phoenix_live_view) f189b759
  • Operating system:
  • Windows
  • MacOS
  • Linux
  • Other (please specify):
  • Browsers (including version) you attempted to reproduce this bug on (the more the merrier):
This occurs during testing, no browser was used. 

Actual behavior

There is a test in which we render_upload two documents, but do not submit the form. This test has intermittent failures. See the stacktrace below.

test "add and remove document", %{conn: conn, project: project} do
      {:ok, view, _html} = live(conn, ~p"/organizations/#{project.team_id}/documents")

      # Open the upload modal and select 2 documents
      view |> element("button[phx-click='open_upload_modal']") |> render_click()

      docs =
        file_input(view, "#upload_form", :documents, [
          %{
            last_modified: 1_594_171_879_000,
            name: "hamlet.txt",
            content: File.read!("test/support/fixtures/document_fixtures/hamlet.txt"),
            type: "text/plain"
          },
          %{
            last_modified: 1_594_171_879_000,
            name: "hamlet2.txt",
            content: File.read!("test/support/fixtures/document_fixtures/hamlet.txt"),
            type: "text/plain"
          }
        ])

      # complete the upload for both docs and make sure their names appear on the page
      assert render_upload(docs, "hamlet.txt") =~ "hamlet.txt"
      assert render_upload(docs, "hamlet2.txt") =~ "hamlet2.txt"

      # remove the duplicate hamlet2 doc
      dup_entry = Enum.find(docs.entries, &(&1["name"] == "hamlet2.txt"))
      dup_pill_selector = "[data-role='removable_pill-#{dup_entry["ref"]}']"
      view |> element(dup_pill_selector) |> render_click()

      # make sure it's gone
      refute render(view) =~ "hamlet2.txt"
      refute has_element?(view, dup_pill_selector)
    end
 ** (EXIT from #PID<0.8947.0>) shutdown: {:channel_upload_exit, {:function_clause, [{Phoenix.LiveViewTest.UploadClient,
:handle_info, [{:socket_close, #PID<0.9033.0>, {:shutdown, :closed}}, %{config: %{chunk_size: 1048576, max_entries: 10, 
chunk_timeout: 10000, max_file_size: 100000000}, socket: %Phoenix.Socket{assigns: %{}, channel: nil, channel_pid: nil, 
endpoint: RevelryWeb.Endpoint, handler: Phoenix.LiveView.Socket, id: nil, joined: false, join_ref: nil, private: %
{connect_info: %{}}, pubsub_server: Revelry.PubSub, ref: nil, serializer: Phoenix.ChannelTest.NoopSerializer, topic: nil, 
transport: {Phoenix.ChannelTest, #PID<0.9013.0>}, transport_pid: #PID<0.9030.0>}, entries: %{"hamlet.txt" => %{name: 
"hamlet.txt", size: 191725, type: "text/plain", socket: %Phoenix.Socket{assigns: %{writer: 
Phoenix.LiveView.UploadTmpFileWriter, done?: false, chunk_timeout: 10000, max_file_size: 191725, chunk_timer: nil, 
writer_state: %{file: #PID<0.9032.0>, path: "/var/folders/lm/vtl4b1n93430tmp88q4mthj00000gn/T/plug-1684-
6NyZ/live_view_upload-1765912529-571794266570-2"}, writer_closed?: false, uploaded_size: 0, live_view_pid: 
#PID<0.9015.0>}, channel: Phoenix.LiveView.UploadChannel, channel_pid: #PID<0.9031.0>, endpoint: 
RevelryWeb.Endpoint, handler: Phoenix.LiveView.Socket, id: nil, joined: true, join_ref: 78915, private: %{log_handle_in: 
false, log_join: :info, connect_info: %{}}, pubsub_server: Revelry.PubSub, ref: nil, serializer: 
Phoenix.ChannelTest.NoopSerializer, topic: "lvu:123", transport: {Phoenix.ChannelTest, #PID<0.9013.0>}, transport_pid: 
#PID<0.9030.0>}, token: 
"SFMyNTY.g2gDaAJhBnQAAAADdwNwaWRYdw1ub25vZGVAbm9ob3N0AAAjNwAAAAAAAAAAdwNyZWZoAm0AAAAUcG
h4LUdJSElGdEgxTmlXZHhUUENtAAAABTc4ODE5dwNjaWRhBW4GAF_JliibAWIAAVGA.Nm8T0fm7gUEYxLyQIY-
Qi9R31apFGPkXLyixzHo5o9Y", ref: "78819", content: ... (removed content for readability of logs) <> ..., chunk_percent: 
100, chunk_boundaries: %{39 => 74773, 74 => 141877, 59 => 113118, 69 => 132291, 67 => 128456, 45 => 86277, 50 => 
95863, 22 => 42180, 51 => 97780, 26 => 49849, 63 => 120787, 47 => 90111, 85 => 162967, 27 => 51766, 77 => 147629, 
0 => 0, 5 => 9587, 21 => 40263, 86 => 164884, ...}}, ...}, ...}], ...}, ...]}} 

I added to lib/phoenix_live_view/test/upload_client.ex

def handle_info({:socket_close, _pid, _reason}, state) do
  {:noreply, state}
end 

which caused the tests to pass. The uploadClient should be able to handle a :socket_close as this is the message sent from a uploadChannel
/lib/phoenix_live_view/upload_channel.ex

def handle_call(:cancel, from, socket) do
    if socket.assigns.writer_closed? do
      GenServer.reply(from, :ok)
      {:stop, {:shutdown, :closed}, socket}  # <-- Channel stops here
    else
      case close_writer(socket, :cancel) do
        {:ok, new_socket} ->
          GenServer.reply(from, :ok)
          {:stop, {:shutdown, :closed}, new_socket}  # <-- Or here
        # ...
      end
    end
  end

Expected behavior

I expect for the test to not crash, even if I do not submit the form in the test.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions