Skip to content

Instantly share code, notes, and snippets.

@TylerPachal
Created July 23, 2021 17:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TylerPachal/9599485f9a103aeb3dda5f16419feb21 to your computer and use it in GitHub Desktop.
Save TylerPachal/9599485f9a103aeb3dda5f16419feb21 to your computer and use it in GitHub Desktop.
Comparing the Ecto return value to what Phoenix renders
# When using Ecto Schemas for validating, I wanted to see what error information was present in the
# Ecto.Changeset, and then compare that to what Phoenix would render by default.
#
# Q: Can you get the index of nested objects' errors?
# A: Yes, for nested lists anything valid will have an empty entry in the list, while invalid
# objects will have some error information. Either way you could use Enum.with_index() to figure
# out a specific index.
## lib/phoenix_playground/accounts/user.ex
defmodule PhoenixPlayground.Accounts.User do
use Ecto.Schema
import Ecto.Changeset
alias PhoenixPlayground.Accounts.Pet
embedded_schema do
field :age, :integer
field :name, :string
embeds_many :pets, Pet
timestamps()
end
@doc false
def changeset(user, attrs) do
user
|> cast(attrs, [:name, :age])
|> cast_embed(:pets, required: true)
|> validate_required([:name, :age])
|> validate_number(:age, greater_than_or_equal_to: 0, less_than: 110)
|> validate_length(:pets, min: 1)
end
end
## lib/phoenix_playground/accounts/pet.ex
defmodule PhoenixPlayground.Accounts.Pet do
use Ecto.Schema
import Ecto.Changeset
embedded_schema do
field :type, :string
field :name, :string
timestamps()
end
@doc false
def changeset(user, attrs) do
user
|> cast(attrs, [:name, :type])
|> validate_required([:name, :type])
|> validate_inclusion(:type, ["dog", "cat"])
end
end
## lib/phoenix_playground_web/controllers/user_controller.ex
defmodule PhoenixPlaygroundWeb.UserControllerTest do
use PhoenixPlaygroundWeb.ConnCase
alias PhoenixPlayground.Accounts.User
setup %{conn: conn} do
{:ok, conn: put_req_header(conn, "accept", "application/json")}
end
# ----- Missing Fields -----
@missing_fields %{}
test "schema - create - missing fields" do
%Ecto.Changeset{errors: errors} = User.changeset(%User{}, @missing_fields)
assert [
name: {"can't be blank", [validation: :required]},
age: {"can't be blank", [validation: :required]},
pets: {"can't be blank", [validation: :required]}
] == errors
end
test "API - create - missing fields", %{conn: conn} do
conn = post(conn, Routes.user_path(conn, :create), user: @missing_fields)
assert %{
"age" => ["can't be blank"],
"name" => ["can't be blank"],
"pets" => ["can't be blank"]
} == json_response(conn, 422)["errors"]
end
# ----- Incorrect Types -----
@incorrect_types %{"name" => false, "age" => [], "pets" => "dogs are great"}
test "schema - create - incorrect types" do
%Ecto.Changeset{errors: errors} = User.changeset(%User{}, @incorrect_types)
assert [
pets: {"is invalid", [validation: :embed, type: {:array, :map}]},
name: {"is invalid", [type: :string, validation: :cast]},
age: {"is invalid", [type: :integer, validation: :cast]}
] == errors
end
test "API - create - incorrect types", %{conn: conn} do
conn = post(conn, Routes.user_path(conn, :create), user: @incorrect_types)
assert %{
"age" => ["is invalid"],
"name" => ["is invalid"],
"pets" => ["is invalid"]
} == json_response(conn, 422)["errors"]
end
# ----- Nested Error -----
@nested_error %{
"name" => "tyler",
"age" => 28,
"pets" => [
%{"name" => "rio", "type" => "dog"},
%{"name" => "frank", "type" => "hotdog"},
%{"name" => "joshua", "type" => "cat"},
%{"name" => "fluffy", "type" => "lobster"}
]
}
test "schema - create - nested error" do
%Ecto.Changeset{valid?: false, changes: %{pets: pets}} = User.changeset(%User{}, @nested_error)
assert [
%{valid?: true},
%{valid?: false},
%{valid?: true},
%{valid?: false}
] = pets
end
test "API - create - nested error", %{conn: conn} do
conn = post(conn, Routes.user_path(conn, :create), user: @nested_error)
assert %{
"pets" => [
%{},
%{"type" => ["is invalid"]},
%{},
%{"type" => ["is invalid"]}
]
} == json_response(conn, 422)["errors"]
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment