- Bug #12: Lower rate limit to 15/min - Bug #13: Wrap model loading in Task.start - Bug #14: Fix classify_time_period guard gap at -3.0 - Bug #15: Not applicable (Elixir has no ?? operator) - Bug #16: Remove fallback Repo.get for station preload - Bug #17: Extract cache_key() in ContactMapController - Bug #18: Add has_many :contacts and :beacons to User schema - A&D #4: Move serve_markdown_if_requested after secure headers - Config #1: Move signing_salt to runtime.exs env var - Config #2: Use --check-unused instead of --unused - Config #3: Re-enable UnsafeToAtom Credo check - Config #5: Re-enable LeakyEnvironment Credo check - Add @spec annotations to fix re-enabled Specs violations - Replace String.to_atom with to_existing_atom where guarded
104 lines
3 KiB
Elixir
104 lines
3 KiB
Elixir
defmodule MicrowavepropWeb.Api.ErrorJSONTest do
|
|
use ExUnit.Case, async: true
|
|
|
|
import Plug.Conn
|
|
import Plug.Test
|
|
|
|
alias Ecto.Changeset
|
|
alias MicrowavepropWeb.Api.ErrorJSON
|
|
|
|
defmodule Dummy do
|
|
@moduledoc false
|
|
use Ecto.Schema
|
|
|
|
import Changeset
|
|
|
|
embedded_schema do
|
|
field :name, :string
|
|
field :age, :integer
|
|
end
|
|
|
|
@spec cs(map()) :: Changeset.t()
|
|
def cs(attrs) do
|
|
%__MODULE__{}
|
|
|> cast(attrs, [:name, :age])
|
|
|> validate_required([:name])
|
|
|> validate_number(:age, greater_than: 0)
|
|
end
|
|
end
|
|
|
|
describe "send_problem/4" do
|
|
test "sets content-type, status, halts, and emits a problem+json body" do
|
|
conn = conn(:get, "/")
|
|
|
|
conn = ErrorJSON.send_problem(conn, 401, "unauthorized", "Token missing.")
|
|
|
|
assert conn.halted
|
|
assert conn.status == 401
|
|
assert get_resp_header(conn, "content-type") == ["application/problem+json; charset=utf-8"]
|
|
|
|
assert %{
|
|
"type" => "about:blank",
|
|
"title" => "unauthorized",
|
|
"status" => 401,
|
|
"detail" => "Token missing."
|
|
} = Jason.decode!(conn.resp_body)
|
|
end
|
|
|
|
test "merges extra fields into the body" do
|
|
conn = ErrorJSON.send_problem(conn(:get, "/"), 422, "x", "y", %{errors: %{a: ["b"]}})
|
|
|
|
assert %{"errors" => %{"a" => ["b"]}} = Jason.decode!(conn.resp_body)
|
|
end
|
|
end
|
|
|
|
describe "send_changeset/2" do
|
|
test "renders flat field error map at status 422" do
|
|
cs = Dummy.cs(%{age: -1})
|
|
|
|
conn = ErrorJSON.send_changeset(conn(:get, "/"), cs)
|
|
|
|
assert conn.status == 422
|
|
body = Jason.decode!(conn.resp_body)
|
|
assert body["title"] == "validation_failed"
|
|
assert %{"name" => ["can't be blank"], "age" => ["must be greater than 0"]} = body["errors"]
|
|
end
|
|
end
|
|
|
|
describe "translate_errors/1" do
|
|
test "interpolates option placeholders into messages" do
|
|
cs =
|
|
%Changeset{}
|
|
|> Map.put(:errors, name: {"too short %{count}", [count: 3]})
|
|
|> Map.put(:types, %{name: :string})
|
|
|> Map.put(:data, %{})
|
|
|
|
assert %{name: ["too short 3"]} = ErrorJSON.translate_errors(cs)
|
|
end
|
|
end
|
|
|
|
describe "render/2" do
|
|
for {tpl, expected_status, expected_title} <- [
|
|
{"400.json", 400, "bad_request"},
|
|
{"401.json", 401, "unauthorized"},
|
|
{"403.json", 403, "forbidden"},
|
|
{"404.json", 404, "not_found"},
|
|
{"405.json", 405, "method_not_allowed"},
|
|
{"415.json", 415, "unsupported_media_type"},
|
|
{"429.json", 429, "too_many_requests"},
|
|
{"500.json", 500, "internal_server_error"}
|
|
] do
|
|
test "produces a problem map for #{tpl}" do
|
|
body = ErrorJSON.render(unquote(tpl), %{})
|
|
assert body.status == unquote(expected_status)
|
|
assert body.title == unquote(expected_title)
|
|
end
|
|
end
|
|
|
|
test "falls back generically for unknown templates" do
|
|
body = ErrorJSON.render("418.json", %{})
|
|
assert body.status == 418
|
|
assert body.title == "im_a_teapot"
|
|
end
|
|
end
|
|
end
|