defmodule AprsmeWeb.Api.V1.FallbackControllerTest do
  use AprsmeWeb.ConnCase, async: true

  import Phoenix.Controller, only: [put_format: 2]

  alias AprsmeWeb.Api.V1.FallbackController

  defp invalid_changeset do
    %Aprsme.Devices{} |> Aprsme.Devices.changeset(%{}) |> Map.put(:action, :insert)
  end

  # Phoenix's render/3 looks up `_format` in conn.params. The bare build_conn
  # hasn't fetched params — put_format/2 short-circuits that lookup so the
  # fallback controller can be exercised in isolation.
  defp prep(conn), do: put_format(conn, "json")

  defp json(conn), do: Jason.decode!(conn.resp_body)

  describe "call/2" do
    test "handles {:error, changeset}", %{conn: conn} do
      conn = FallbackController.call(prep(conn), {:error, invalid_changeset()})
      assert conn.status == 422
      body = json(conn)
      assert body["error"]["code"] == "validation_error"
      assert Map.has_key?(body["error"]["details"], "identifier")
    end

    test "handles {:error, :not_found}", %{conn: conn} do
      conn = FallbackController.call(prep(conn), {:error, :not_found})
      assert conn.status == 404
      assert json(conn)["error"]["code"] == "not_found"
    end

    test "handles {:error, :unauthorized}", %{conn: conn} do
      conn = FallbackController.call(prep(conn), {:error, :unauthorized})
      assert conn.status == 401
      assert json(conn)["error"]["code"] == "generic_error"
    end

    test "handles {:error, :forbidden}", %{conn: conn} do
      conn = FallbackController.call(prep(conn), {:error, :forbidden})
      assert conn.status == 403
    end

    test "handles {:error, :bad_request}", %{conn: conn} do
      conn = FallbackController.call(prep(conn), {:error, :bad_request})
      assert conn.status == 400
    end

    test "unknown atom reasons produce 500", %{conn: conn} do
      conn = FallbackController.call(prep(conn), {:error, :totally_unknown})
      assert conn.status == 500
    end

    test "handles {:error, atom, message}", %{conn: conn} do
      conn = FallbackController.call(prep(conn), {:error, :bad_request, "specific message"})
      assert conn.status == 400
      assert json(conn)["error"]["message"] == "specific message"
    end

    test "handles {:error, binary}", %{conn: conn} do
      conn = FallbackController.call(prep(conn), {:error, "custom error"})
      assert conn.status == 400
      assert json(conn)["error"]["message"] == "custom error"
    end

    test "handles :timeout", %{conn: conn} do
      conn = FallbackController.call(prep(conn), :timeout)
      assert conn.status == 408
    end

    test "handles unknown errors with 500", %{conn: conn} do
      conn = FallbackController.call(prep(conn), :some_weird_thing)
      assert conn.status == 500
      assert json(conn)["error"]["code"] == "internal_server_error"
    end
  end
end
