Hello!
I noticed that if I have a LV test that uses the Ecto sandbox in manual mode, the LV process in the test has “magically” access to the sandboxed DB connection created by the test process. This is really amazing but I don’t understand how it works
Simplified example:
Liveview:
defmodule MyAppWeb.LiveView do
@moduledoc false
import Ecto.Query
use MyAppWeb, :live_view
@impl true
def mount(_params, _session, socket) do
connect_params = get_connect_params(socket)
IO.inspect(connect_params, label: "connect_params")
IO.inspect(self(), label: "I'm the LV")
Repo.all(MyApp.User) |> length() |> IO.inspect(label: "users")
{:ok, socket}
end
end
Test setup:
defmodule MyAppWeb.ConnCase do
...
setup tags do
Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, :manual)
:ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo)
{:ok, conn: Phoenix.ConnTest.build_conn()}
end
end
The actual test:
defmodule MyAppWeb.LiveViewTest do
use MyAppWeb.ConnCase
test "just to prove a point", %{conn: conn} do
insert_test_records_in_user_table(20)
IO.inspect(self(), label: "I'm the test process")
{:ok, view, html} = live(conn, "/live_view")
end
end
Output:
1 I'm the test process: #PID<0.869.0>
2 connect_params: nil
3 I'm the LV: #PID<0.869.0>
4 entries: 20
5 connect_params: %{"_mounts" => 0}
6 I'm the LV: #PID<0.873.0>
7 entries: 20
Lines 2-4 come from the static LV mount, and lines 5-7 from the connected one. As you can see, the static mount happens in the same process as the test’s, while the connected mount happens in a different process. So far nothing strange. What I don’t understand is how the connected LV can see the test records inserted by the test process, even though I didn’t change the mode of the sandbox to shared
or call Ecto.Adapters.SQL.Sandbox.allow
. I would have expected the DB access from the connected LV process to fail with the error:
** (DBConnection.OwnershipError) cannot find ownership process for #PID<0.873.0>
The only explanation I can think of is that the sandboxed DB connection is somehow passed over to the LV process when this latter is spawned, by calling Ecto.Adapters.SQL.Sandbox.allow
somewhere. But I couldn’t find where this happens in the code.
Can someone please clarify this for me?