defmodule Tarakan.RepositoryCode.CacheTest do
  use ExUnit.Case, async: false

  alias Tarakan.RepositoryCode.Cache

  setup do
    Cache.clear()
    :ok
  end

  test "a waiter is promoted to leader when the flight leader dies" do
    key = {:hosted_commit, 999_999, String.duplicate("f", 40)}
    test_pid = self()

    leader =
      spawn(fn ->
        Cache.fetch(key, 60_000, fn ->
          send(test_pid, :leader_fetching)
          Process.sleep(:infinity)
        end)
      end)

    assert_receive :leader_fetching, 1_000

    waiter =
      Task.async(fn ->
        Cache.fetch(key, 60_000, fn -> {:ok, :from_waiter} end)
      end)

    wait_for_waiter(key)
    Process.exit(leader, :kill)

    assert Task.await(waiter, 5_000) == {:ok, :from_waiter}
  end

  defp wait_for_waiter(key, attempts \\ 50)

  defp wait_for_waiter(key, 0), do: flunk("waiter never joined the flight for #{inspect(key)}")

  defp wait_for_waiter(key, attempts) do
    if match?(%{waiters: [_ | _]}, Map.get(:sys.get_state(Cache).inflight, key)) do
      :ok
    else
      Process.sleep(10)
      wait_for_waiter(key, attempts - 1)
    end
  end
end
