Skip to content

Commit

Permalink
fix: return correct changeset params when foreign key is set
Browse files Browse the repository at this point in the history
  • Loading branch information
cylkdev committed Nov 24, 2022
1 parent 8cdc270 commit 04b0fa7
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 16 deletions.
61 changes: 61 additions & 0 deletions GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
## Designing your Context

Let's talk about how we write our context.

### When in doubt, sort it out.

You should never rely on internal implementation details of the underlying stdlib as they may change without notice. When you insert a `has-many` or `many-to-many` association using `Ecto.Changeset.cast_assoc/2` the order of the values returned are not guaranteed to be in the same order as the values inserted. This means we need to sort the data after insertion, Let's look at an example:

```elixir
defmodule FactoryExExampleTest do
use FactoryEx.DataCase, async: true

def find_admin_user_account(users), do: Enum.filter(users, &user_account_is_admin?/1)
def all_non_admin_user_accounts(users), do: Enum.reject(users, &user_account_is_admin?/1)
def user_account_is_admin?(user), do: user.account.admin === true

def create_company_users_accounts(_context) do
# Create 3 Users. Each User Requires an account. One user will have an admin
# account and the other has regular user privileges.
users_params = [
%{account: %{admin: true}},
%{},
%{}
]
# Create a company with 3 users
company_params = %{
name: "example_company_name",
users: users_params
}

# These fields will have their appropriate Ecto.Schema
# factory module's build function called.
relational_fields = [
users: [:account]
]

# set `validate` to false as the changeset requires an ID and we're making
# an insert so id does not exist. This is used to bypass the changeset behaviour.
company = FactoryEx.build(SchemasPg.Support.Factory.Accounts.Company, company_params, relational: relational_fields, validate: false)

# Sort and create the context for tests
users = company.users

user_account_admin = find_admin_user_account(users)
[user_account_one, user_account_two] = all_non_admin_user_accounts(users)

%{
company: company,
user_account_admin: user_account_admin,
user_account_one: user_account_one,
user_account_two: user_account_two
}
end

setup [:create_company_users_accounts]

test "outputs context result", context do
IO.inspect(context, label: "hooray!")
end
end
```
29 changes: 13 additions & 16 deletions lib/factory_ex/schema_associations_builder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -66,18 +66,18 @@ defmodule FactoryEx.SchemaAssociationsBuilder do
maybe_create_many_new_assocs(
ecto_assoc.queryable,
params,
ecto_assoc.owner_key,
field,
assoc_keys,
ecto_assoc.owner_key
assoc_keys
)

ecto_assoc.cardinality === :one ->
maybe_create_new_assoc(
ecto_assoc.queryable,
params,
ecto_assoc.owner_key,
field,
assoc_keys,
ecto_assoc.owner_key
assoc_keys
)

end
Expand All @@ -89,20 +89,19 @@ defmodule FactoryEx.SchemaAssociationsBuilder do
convert_to_ecto_schema_assoc_params({field, []}, params, ecto_assocs, ecto_schema)
end

defp maybe_create_many_new_assocs(queryable, params, field, assoc_keys, owner_key) do
defp maybe_create_many_new_assocs(queryable, params, owner_key, field, assoc_keys) do
changeset_params = Map.get(params, field, [%{}])
case Map.get(params, owner_key) do
nil ->
params = Map.get(params, field, %{})
factory_reflections_build_many(queryable, params, assoc_keys)

_owner_key -> params
nil -> factory_reflections_build_many(queryable, changeset_params, assoc_keys)
_foreign_key -> changeset_params
end
end

defp maybe_create_new_assoc(queryable, params, field, assoc_keys, owner_key) do
defp maybe_create_new_assoc(queryable, params, owner_key, field, assoc_keys) do
changeset_params = Map.get(params, field, %{})
case Map.get(params, owner_key) do
nil -> factory_reflections_build(queryable, params, field, assoc_keys)
_owner_key -> params
nil -> factory_reflections_build(queryable, changeset_params, assoc_keys)
_foreign_key -> changeset_params
end
end

Expand All @@ -124,9 +123,7 @@ defmodule FactoryEx.SchemaAssociationsBuilder do
|> Enum.map(&Map.merge(&1, assoc_params))
end

defp factory_reflections_build(queryable, params, field, assoc_keys) do
params = Map.get(params, field, %{})

defp factory_reflections_build(queryable, params, assoc_keys) do
assoc_params = create_ecto_schema_reflections(queryable, params, assoc_keys)
params = QueryableEtsFactory.build_params(queryable, params)

Expand Down
25 changes: 25 additions & 0 deletions test/support/data_case.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
defmodule FactoryEx.DataCase do
@moduledoc false
use ExUnit.CaseTemplate

using do
quote do
alias FactoryEx.Support.Repo

import Ecto
import Ecto.Changeset
import Ecto.Query
import FactoryEx.DataCase
end
end

setup tags do
:ok = Ecto.Adapters.SQL.Sandbox.checkout(FactoryEx.Support.Repo)

unless tags[:async] do
Ecto.Adapters.SQL.Sandbox.mode(FactoryEx.Support.Repo, {:shared, self()})
end

:ok
end
end

0 comments on commit 04b0fa7

Please sign in to comment.