Skip to content

Commit

Permalink
fix: mocks for supervised tasks (#750)
Browse files Browse the repository at this point in the history
Fix mocks for supervised tasks
  • Loading branch information
tomekowal authored Feb 3, 2025
1 parent 15f69d7 commit 2f6b2a6
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 16 deletions.
21 changes: 5 additions & 16 deletions lib/tesla/mock.ex
Original file line number Diff line number Diff line change
Expand Up @@ -229,23 +229,12 @@ defmodule Tesla.Mock do

# Gets the mock fun for the current process or its ancestors
defp pdict_get do
pid_holder =
Enum.find(Process.get(:"$ancestors", []), self(), fn ancestor ->
is_holder? =
ancestor
|> Process.info()
|> Keyword.get(:dictionary)
|> Keyword.get(__MODULE__)

!is_nil(is_holder?)
end)
|> case do
nil -> raise "Unknown pid_holder in mock"
pid when is_pid(pid) -> pid
name when is_atom(name) -> Process.whereis(name)
end
potential_mock_holder_pids = [self() | Enum.reverse(Process.get(:"$callers", []))]

pid_holder |> Process.info() |> Keyword.get(:dictionary) |> Keyword.get(__MODULE__)
Enum.find_value(potential_mock_holder_pids, nil, fn pid ->
{:dictionary, process_dictionary} = Process.info(pid, :dictionary)
Keyword.get(process_dictionary, __MODULE__)
end)
end

defp agent_set(fun) do
Expand Down
35 changes: 35 additions & 0 deletions test/tesla/mock_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,41 @@ defmodule Tesla.MockTest do
end
end

describe "supervised task" do
test "allows mocking in the caller process" do
# in real apps, task supervisor will be part of the supervision tree
# and it won't be an ancestor of the test process
# to simulate that, we will set the mock in a task
#
# test_process
# |-mocking_task will set the mock and create the supervised task
# `-task supervisor
# `- supervised_task
# this way, mocking_task is not an $ancestor of the supervised_task
# but it is $caller
{:ok, supervisor_pid} = start_supervised(Task.Supervisor, restart: :temporary)

mocking_task =
Task.async(fn ->
mock(fn
%{url: "/callers-test"} ->
{:ok, %Tesla.Env{status: 200, body: "callers work"}}
end)

supervised_task =
Task.Supervisor.async(supervisor_pid, fn ->
assert {:ok, %Tesla.Env{} = env} = Client.get("/callers-test")
assert env.status == 200
assert env.body == "callers work"
end)

Task.await(supervised_task)
end)

Task.await(mocking_task)
end
end

describe "without mock" do
test "raise on unmocked request" do
assert_raise Tesla.Mock.Error, fn ->
Expand Down

0 comments on commit 2f6b2a6

Please sign in to comment.