0
votes

I have a test in my Phoenix app that is testing a Phoenix.PubSub subscriber that uses Genserver. The subscriber does some database work as part of its handle_info/2.

test "sending creating a referral code upon user registration" do
  start_supervised(MyApp.Accounts.Subscriber)
  user = insert(:user)

  Phoenix.PubSub.broadcast(MappApp.PubSub, "accounts", {:register, user})

  assert_eventually(Repo.exists?(ReferralCode))

  stop_supervised(MyApp.Accounts.Subscriber)
end

Running this test module by itself is fine. However when I run my entire test suite I get an error like so (the test still passes):

[error] GenServer MyApp.Accounts.Subscriber terminating
** (stop) exited in: DBConnection.Holder.checkout(#PID<0.970.0>, [log: #Function<9.124843621/1 in Ecto.Adapters.SQL.with_log/3>, cache_statement: "ecto_insert_referral_codes", timeout: 15000, pool_size: 10, pool: DBConnection.Ownership])
    ** (EXIT) shutdown: "owner #PID<0.969.0> exited"
    <stacktrace...>

This looks like it's an issue with the database connection still being open when the process is terminated so it doesn't die gracefully. But I'm not sure how to deal with this.

Any advice on how to prevent this error?

1
Do you by any chance run this test module with async: true? If so, that could be the culprit.zwippie
I'm useing the ModelCase module. Is this the default behaviour?harryg
If you just have use ModelCase (and not use ModelCase, async: true) then the tests in the module will not run concurrently with tests in other modules, which is probably what you want. (Although I don't know what your ModelCase looks like)zwippie
It's basically stock (now called DataCase in current Phoenix: github.com/phoenixframework/phoenix/blob/master/installer/…).harryg
I tried with and without async: false and the same error occurs. I think the ModelCase runs the test non-asyncharryg

1 Answers

0
votes

I ran into this today. Any time you are doing database operations in separate child processes (e.g. database operations triggered inside a GenStage or GenServer et al), then you need to read the Sandbox adapter documentation carefully. There is an FAQ that deals with this error specifically, and the solution can be either to explicitly grant the Sandbox adapter access to the child process like this (from the docs):

test "gets results from GenServer" do
  {:ok, pid} = MyAppServer.start_link()
  Ecto.Adapters.SQL.Sandbox.allow(Repo, self(), pid)
  assert MyAppServer.get_my_data_fast(timeout: 1000) == [...]
end

or you can enabled "shared" mode by altering your test setup to set the Sandbox mode:

  setup do
    :ok = Sandbox.checkout(Repo)
    Sandbox.mode(Repo, {:shared, self()})
  end

I had more luck with the latter, but be aware that if you are using some other adapter anywhere explicitly (e.g. to make raw database calls), then it might cause tests to fail (because it can no longer get a connection).