elixir-wallaby / wallaby

Concurrent browser tests for your Elixir web apps.
https://twitter.com/elixir_wallaby
MIT License
1.68k stars 198 forks source link

How to send API Requests in parallel? #781

Closed phortx closed 5 months ago

phortx commented 5 months ago

Elixir and Erlang/OTP versions

Erlang/OTP 26 [erts-14.2.5] [source] [64-bit] [smp:11:11] [ds:11:11:10] [async-threads:1] [jit]

Elixir 1.16.3 (compiled with Erlang/OTP 24)

Operating system

MacOS

Browser

Chrome

Driver

ChromeDriver

Correct Configuration

Current behavior

Hi,

this might not be exactly related to wallaby, but is an interesting use case which could be relevant to other wallaby users too.

I'm writing feature tests for my payment system. The user flow is pretty simple: The user logs in, clicks on the payment button and then is redirect to the payment provider (Stripe, LemonSqueezy, whatever). This can be tested straight forward with wallaby.

Now when finishing the payment process, the provider does an Webhook call to the API of my app. I simulate that by simple extracting the url of the running server from the wallaby session and calling the webhook via Req:

current_url(session)
|> URI.parse()
|> send_webhook_request([...])

That works very well and the API call is handled by the according plug.

The challenge here however is to get the Ecto Sandbox playing along. The API webhook request produces the following error as soon as the Repo is accessed:

13:13:41.218 [error] #PID<0.685.0> running CassiopeiaWeb.Endpoint (connection #PID<0.684.0>, stream id 1) terminated
Server: localhost:4002 (http)
Request: POST /webhooks/lemonsqueezy
** (exit) an exception was raised:
    ** (DBConnection.OwnershipError) cannot find ownership process for #PID<0.685.0>.

When using ownership, you must manage connections in one
of the four ways:
[...]

If you are reading this error, it means you have not done one
of the steps above or that the owner process has crashed.

See Ecto.Adapters.SQL.Sandbox docs for more information.
        (ecto_sql 3.11.2) lib/ecto/adapters/sql.ex:1051: Ecto.Adapters.SQL.raise_sql_call_error/1
        [...]

I'm using the LemonEx Plug in my endpoint.ex for webhook handling of LemonSqueezy calls:

  plug LemonEx.Webhooks.Plug,
    at: "/webhooks/lemonsqueezy",
    handler: CassiopeiaWeb.Webhooks.LemonSqueezy

Expected behavior

I'm wonder how to get the Ecto Sandbox working with that scenario. Do you have any idea?

Any help would be really appreciated. Thanks!

phortx commented 5 months ago

I'm not sure wether I fully understand how the sandbox is actually working, but seems like the issue here is that the request is handled by the LemonEx Plug outside of the phoenix routing and thus the sandbox is not correct setup. I've played around with several code snippets from the documentation for enabling the Ecto Sandbox for LiveView, but couldn't produce a working solution 🤔

mhanberg commented 5 months ago

Basically you'll want to add the relevant metadata to the useragent string in your request (you can use the functions in this module to create them https://hexdocs.pm/phoenix_ecto/4.6.1/Phoenix.Ecto.SQL.Sandbox.html) and make sure that it goes through the sandbox plug that is in your endpoint

mhanberg commented 5 months ago

To clarify (because the docs I listed show how to set it up for "external clients"), the wallaby test has already checked out a db connection for your test, and ensured that the LV/controller process uses that connection.

The problem is that your webhook request controller doesn't know what db connection to checkout, but that is what the user agent meta string will tell the sandbox plug what to do

phortx commented 5 months ago

To clarify (because the docs I listed show how to set it up for "external clients"), the wallaby test has already checked out a db connection for your test, and ensured that the LV/controller process uses that connection.

You're right, that's what I figured out too.

I think I solved the issue by adding:

metadata =
      MyApp.Repo
      |> Phoenix.Ecto.SQL.Sandbox.metadata_for(self())
      |> Phoenix.Ecto.SQL.Sandbox.encode_metadata()

and then passing this as user-agent header to the api request. Seems to work :)

Thank you very much for your help!