From 68f71771602bce1a90c9a516af2fd7b8b14e3c55 Mon Sep 17 00:00:00 2001 From: Arno Dirlam Date: Mon, 13 May 2024 21:01:22 +0200 Subject: [PATCH] Rename to elixir-dx/refactory and traits (was infer-beam/refinery and refinements) --- .formatter.exs | 2 +- .gitignore | 2 +- CHANGELOG.md | 6 +- README.md | 115 +++++++- config/config.exs | 4 +- config/test.exs | 8 +- lib/refactory.ex | 151 +++++++++++ lib/refinery.ex | 248 ------------------ mix.exs | 12 +- .../assignments_test.exs | 14 +- .../{refinery => refactory}/defaults_test.exs | 12 +- test/{refinery => refactory}/plain_test.exs | 6 +- test/refactory/refinements_test.exs | 150 +++++++++++ test/refinery/refinements_test.exs | 114 -------- test/repo.ex | 4 +- test/schema/list.ex | 4 +- test/schema/list_tag.ex | 4 +- test/schema/list_template.ex | 4 +- .../20220126160251_create_base_schema.exs | 2 +- test/schema/task.ex | 4 +- test/schema/user.ex | 4 +- test/support/data_case.ex | 10 +- test/test_helper.exs | 4 +- 23 files changed, 463 insertions(+), 421 deletions(-) create mode 100644 lib/refactory.ex delete mode 100644 lib/refinery.ex rename test/{refinery => refactory}/assignments_test.exs (89%) rename test/{refinery => refactory}/defaults_test.exs (92%) rename test/{refinery => refactory}/plain_test.exs (96%) create mode 100644 test/refactory/refinements_test.exs delete mode 100644 test/refinery/refinements_test.exs diff --git a/.formatter.exs b/.formatter.exs index 0b99a0a..830e633 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -3,7 +3,7 @@ inputs: [ "*.{ex,exs}", "{config,lib}/**/*.{ex,exs}", - "test/{refinery,support}/**/*.{ex,exs}", + "test/{refactory,support}/**/*.{ex,exs}", "test/*.{ex,exs}" ], import_deps: [:ecto], diff --git a/.gitignore b/.gitignore index 6e2b116..48d6186 100644 --- a/.gitignore +++ b/.gitignore @@ -20,7 +20,7 @@ erl_crash.dump *.ez # Ignore package tarball (built via "mix hex.build"). -refinery-*.tar +refactory-*.tar # Temporary files, for example, from tests. /tmp/ diff --git a/CHANGELOG.md b/CHANGELOG.md index aba93b9..93faba4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Refinements modules via `use Refinery, repo: MyApp.Repo` -- Default refinement for each Ecto schema type -- Custom refinements +- Traits modules via `use Refactory, repo: MyApp.Repo` +- Default trait for each Ecto schema type +- Custom traits - Initial docs diff --git a/README.md b/README.md index 4f59193..97d1289 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,127 @@ -# Refinery +# Refactory -An Elixir library to generate test data recursively with refinements +An Elixir library to generate test data recursively with traits ## Installation The package can be installed -by adding `refinery` to your list of dependencies in `mix.exs`: +by adding `refactory` to your list of dependencies in `mix.exs`: ```elixir def deps do [ - {:refinery, "~> 0.1.0", github: "infer-beam/refinery", only: :test} + {:refactory, "~> 0.1.0", only: :test} ] end ``` -Documentation can found at . +Documentation can found at . + + +Refactory allows generating Ecto records with nested overrides for your tests. + +## Factory module + +To start using Refactory, first define a factory module: + +``` +defmodule MyApp.Factory do + use Refactory, repo: MyApp.Repo +end +``` + +## Usage + +The factory module has two functions: + +- `build/2` generates an Ecto record with the given traits applied +- `create/2` inserts an Ecto record into the database + +## Traits + +A trait can be +- a `Map` in which each key-value pair is either + - a field with its value + - an association with a trait (for `belongs_to`, `has_one`, and `embeds_one`) + - _soon:_ an association with a list of traits (for `has_many` and `embeds_many`) +- a custom trait defined in the factory module (see below) +- a `Tuple` with multiple traits to be applied + +## Basic example + +``` +defmodule MyApp.Factory do + use Refactory, repo: MyApp.Repo +end + +MyApp.Factory.build(MyApp.List, %{ + title: "Refined List", + created_by_user: %{email: "test@email.org"} +}) + +%MyApp.List{ + title: "Refined List", + created_by_user: %MyApp.User{ + email: "test@email.org" + } +} +``` + +## Default traits + +Default traits can be defined in the factory module. +They are always applied first. + +``` +defmodule MyApp.Factory do + use Refactory, repo: MyApp.Repo + + def trait(MyApp.List, :default) do + %{ + title: "Default Title" + } + end +end + + +MyApp.Factory.build(MyApp.List) + +%MyApp.List{title: "Default Title"} +``` + +## Custom traits + +Custom traits can be defined in the factory module and then used by their name. + +``` +defmodule MyApp.Factory do + use Refactory, repo: MyApp.Repo + + def trait(MyApp.List, :default) do + %{ + title: "Default Title" + } + end + + def trait(MyApp.List, :with_admin_user) do + %{ + created_by_user: %{ + role: :admin + } + } + end +end + + +MyApp.Factory.build(MyApp.List, :with_admin_user) + +%MyApp.List{title: "Default Title", created_by_user: %MyApp.User{role: :admin}} +``` + + ## Special thanks This project is sponsored and kindly supported by [Team Engine](https://www.teamengine.co.uk/). -If you'd like to join us working on [Infer](https://github.com/infer-beam/infer) and Refinery as a contractor, please [reach out](https://tinyurl.com/engine-infer-dev2). +If you'd like to join us working on [Dx](https://github.com/elixir-dx/dx) and Refactory as a contractor, please reach out to @arnodirlam. diff --git a/config/config.exs b/config/config.exs index ca952e1..29cf7c9 100644 --- a/config/config.exs +++ b/config/config.exs @@ -10,11 +10,11 @@ import Config # You can configure your application as: # -# config :refinery, key: :value +# config :refactory, key: :value # # and access this configuration in your application as: # -# Application.get_env(:refinery, :key) +# Application.get_env(:refactory, :key) # # You can also configure a 3rd-party app: # diff --git a/config/test.exs b/config/test.exs index 8dea520..cc5eb04 100644 --- a/config/test.exs +++ b/config/test.exs @@ -1,6 +1,6 @@ import Config -config :refinery, Refinery.Test.Repo, +config :refactory, Refactory.Test.Repo, hostname: "localhost", username: "postgres", password: "postgres", @@ -8,8 +8,8 @@ config :refinery, Refinery.Test.Repo, pool: Ecto.Adapters.SQL.Sandbox, priv: "test/schema" -config :refinery, - ecto_repos: [Refinery.Test.Repo], - repo: Refinery.Test.Repo +config :refactory, + ecto_repos: [Refactory.Test.Repo], + repo: Refactory.Test.Repo config :logger, level: :warn diff --git a/lib/refactory.ex b/lib/refactory.ex new file mode 100644 index 0000000..6b250ea --- /dev/null +++ b/lib/refactory.ex @@ -0,0 +1,151 @@ +defmodule Refactory do + @external_resource Path.expand("./README.md") + @moduledoc File.read!(Path.expand("./README.md")) + |> String.split("") + |> Enum.at(1) + + defmacro __using__(opts) do + quote location: :keep do + def refinery_repo() do + unquote(opts[:repo]) + end + + def create(type, traits \\ %{}) do + Refactory.create(__MODULE__, type, traits) + end + + def build(type, traits \\ %{}) do + Refactory.build(__MODULE__, type, traits) + end + end + end + + @doc """ + Inserts an Ecto record with the given traits applied into the database + """ + def create(module, type, traits \\ %{}) do + repo = module.refinery_repo() + build(module, type, traits) |> repo.insert!() + end + + @doc """ + Generates an Ecto record with the given traits applied + """ + def build(module, type, traits \\ %{}) do + case resolve_refinement(module, type, {:default, traits}) do + record = %{__struct__: ^module} -> + record + + record = %{__struct__: _module} -> + raise ArgumentError, "Expected a struct of type #{module}. Got #{inspect(record)}" + + attrs -> + do_build(module, type, attrs) + end + end + + defp do_build(module, type, attrs) do + record = struct!(type, attrs) + + # set associations in record + Enum.reduce(attrs, record, fn {name, traits}, record -> + assoc = + case ecto_association(type, name) || ecto_embed(type, name) do + %Ecto.Association.BelongsTo{related: type} -> {:build_one, type} + %Ecto.Association.Has{cardinality: :one, related: type} -> {:build_one, type} + %Ecto.Embedded{cardinality: :one, related: type} -> {:build_one, type} + _ -> :skip + end + + case {assoc, traits} do + {{:build_one, type}, %type{}} -> + record + + {{:build_one, type}, %other_type{}} -> + raise ArgumentError, + "Expected value of type #{type} for #{type}.#{name}. Got #{other_type}" + + {{:build_one, _type}, nil} -> + Map.put(record, name, nil) + + {{:build_one, type}, _} -> + associated_record = build(module, type, traits) + Map.put(record, name, associated_record) + + {:skip, _} -> + record + end + end) + end + + defp resolve_refinement(module, type, :default) do + module.trait(type, :default) + rescue + _e in [UndefinedFunctionError, FunctionClauseError] -> %{} + e -> reraise e, __STACKTRACE__ + end + + defp resolve_refinement(module, type, trait) do + module.trait(type, trait) + rescue + _e in [UndefinedFunctionError, FunctionClauseError] -> + merge_refinements(module, type, trait, %{}) + + e -> + reraise e, __STACKTRACE__ + end + + defp merge_refinements(module, type, traits, result) when is_tuple(traits) do + traits + |> Tuple.to_list() + |> Enum.reduce(result, &deep_merge(&2, resolve_refinement(module, type, &1), false, true)) + end + + defp merge_refinements(_module, _type, traits, result) when is_map(traits) do + deep_merge(result, traits, false) + end + + defp merge_refinements(module, type, trait, _result) do + raise ArgumentError, "Unknown trait for #{type} in #{module}: #{inspect(trait)}" + end + + defp ecto_association(type, name), do: type.__schema__(:association, name) + defp ecto_embed(type, name), do: type.__schema__(:embed, name) + + defp deep_merge(left, right, concat_lists? \\ true, struct_overrides? \\ false) do + Map.merge(left, right, &deep_resolve(&1, &2, &3, concat_lists?, struct_overrides?)) + end + + defp deep_resolve(_key, _left, %{__struct__: _type} = right, _concat_lists?, true) do + right + end + + defp deep_resolve( + _key, + %{__struct__: type} = left, + %{__struct__: type} = right, + _concat_lists?, + _struct_overrides? + ) do + struct!(type, deep_merge(Map.from_struct(left), Map.from_struct(right))) + end + + defp deep_resolve(_key, %{__struct__: type}, _right, _concat_lists?, _struct_overrides?) do + raise ArgumentError, "#{type} cannot be merged with non-#{type}." + end + + defp deep_resolve(_key, _left, %{__struct__: type}, _concat_lists?, _struct_overrides?) do + raise ArgumentError, "Non-#{type} cannot be merged with #{type}." + end + + defp deep_resolve(_key, %{} = left, %{} = right, _concat_lists?, _struct_overrides?) do + deep_merge(left, right) + end + + defp deep_resolve(_key, left, right, true, _struct_overrides?) + when is_list(left) and is_list(right) do + left ++ right + end + + defp deep_resolve(_key, _left, right, _concat_lists?, _struct_overrides?), do: right +end diff --git a/lib/refinery.ex b/lib/refinery.ex deleted file mode 100644 index aa6194e..0000000 --- a/lib/refinery.ex +++ /dev/null @@ -1,248 +0,0 @@ -defmodule Refinery do - @moduledoc """ - Refinery allows generating Ecto records with nested overrides for your tests. - - ## Refinements module - - To start using Refinery, first define a refinements module: - - ``` - defmodule MyApp.Refinements do - use Refinery, repo: MyApp.Repo - end - ``` - - ## Usage - - The refinements module has two functions: - - - `build/2` generates an Ecto record with the given refinements applied - - `create/2` inserts an Ecto record into the database - - ## Refinements - - A refinement can be - - a `Map` in which each key-value pair is either - - a field with its value - - an association with a refinement (for `belongs_to`, `has_one`, and `embeds_one`) - - _soon:_ an association with a list of refinements (for `has_many` and `embeds_many`) - - a custom refinement defined in the refinements module (see below) - - a `Tuple` with multiple refinements to be applied - - ## Basic example - - ``` - defmodule MyApp.Refinements do - use Refinery, repo: MyApp.Repo - end - - MyApp.Refinements.build(MyApp.List, %{ - title: "Refined List", - created_by_user: %{email: "test@email.org"} - }) - - %MyApp.List{ - title: "Refined List", - created_by_user: %MyApp.User{ - email: "test@email.org" - } - } - ``` - - ## Default refinements - - Default refinements can be defined in the refinements module. - They are always applied first. - - ``` - defmodule MyApp.Refinements do - use Refinery, repo: MyApp.Repo - - def refinement(MyApp.List, :default) do - %{ - title: "Default Title" - } - end - end - - - MyApp.Refinements.build(MyApp.List) - - %MyApp.List{title: "Default Title"} - ``` - - ## Custom refinements - - Custom refinements can be defined in the refinements module and then used by their name. - - ``` - defmodule MyApp.Refinements do - use Refinery, repo: MyApp.Repo - - def refinement(MyApp.List, :default) do - %{ - title: "Default Title" - } - end - - def refinement(MyApp.List, :with_admin_user) do - %{ - created_by_user: %{ - role: :admin - } - } - end - end - - - MyApp.Refinements.build(MyApp.List, :with_admin_user) - - %MyApp.List{title: "Default Title", created_by_user: %MyApp.User{role: :admin}} - ``` - """ - - defmacro __using__(opts) do - quote location: :keep do - def refinery_repo() do - unquote(opts[:repo]) - end - - def create(type, refinements \\ %{}) do - Refinery.create(__MODULE__, type, refinements) - end - - def build(type, refinements \\ %{}) do - Refinery.build(__MODULE__, type, refinements) - end - end - end - - @doc """ - Inserts an Ecto record with the given refinements applied into the database - """ - def create(module, type, refinements \\ %{}) do - repo = module.refinery_repo() - build(module, type, refinements) |> repo.insert!() - end - - @doc """ - Generates an Ecto record with the given refinements applied - """ - def build(module, type, refinements \\ %{}) do - case resolve_refinement(module, type, {:default, refinements}) do - record = %{__struct__: ^module} -> - record - - record = %{__struct__: _module} -> - raise ArgumentError, "Expected a struct of type #{module}. Got #{inspect(record)}" - - attrs -> - do_build(module, type, attrs) - end - end - - defp do_build(module, type, attrs) do - record = struct!(type, attrs) - - # set associations in record - Enum.reduce(attrs, record, fn {name, refinements}, record -> - assoc = - case ecto_association(type, name) || ecto_embed(type, name) do - %Ecto.Association.BelongsTo{related: type} -> {:build_one, type} - %Ecto.Association.Has{cardinality: :one, related: type} -> {:build_one, type} - %Ecto.Embedded{cardinality: :one, related: type} -> {:build_one, type} - _ -> :skip - end - - case {assoc, refinements} do - {{:build_one, type}, %type{}} -> - record - - {{:build_one, type}, %other_type{}} -> - raise ArgumentError, - "Expected value of type #{type} for #{type}.#{name}. Got #{other_type}" - - {{:build_one, _type}, nil} -> - Map.put(record, name, nil) - - {{:build_one, type}, _} -> - associated_record = build(module, type, refinements) - Map.put(record, name, associated_record) - - {:skip, _} -> - record - end - end) - end - - defp resolve_refinement(module, type, :default) do - module.refinement(type, :default) - rescue - _e in [UndefinedFunctionError, FunctionClauseError] -> %{} - e -> reraise e, __STACKTRACE__ - end - - defp resolve_refinement(module, type, refinement) do - module.refinement(type, refinement) - rescue - _e in [UndefinedFunctionError, FunctionClauseError] -> - merge_refinements(module, type, refinement, %{}) - - e -> - reraise e, __STACKTRACE__ - end - - defp merge_refinements(module, type, refinements, result) when is_tuple(refinements) do - refinements - |> Tuple.to_list() - |> Enum.reduce(result, &deep_merge(&2, resolve_refinement(module, type, &1), false, true)) - end - - defp merge_refinements(_module, _type, refinements, result) when is_map(refinements) do - deep_merge(result, refinements, false) - end - - defp merge_refinements(module, type, refinement, _result) do - raise ArgumentError, "Unknown refinement for #{type} in #{module}: #{inspect(refinement)}" - end - - defp ecto_association(type, name), do: type.__schema__(:association, name) - defp ecto_embed(type, name), do: type.__schema__(:embed, name) - - defp deep_merge(left, right, concat_lists? \\ true, struct_overrides? \\ false) do - Map.merge(left, right, &deep_resolve(&1, &2, &3, concat_lists?, struct_overrides?)) - end - - defp deep_resolve(_key, _left, %{__struct__: _type} = right, _concat_lists?, true) do - right - end - - defp deep_resolve( - _key, - %{__struct__: type} = left, - %{__struct__: type} = right, - _concat_lists?, - _struct_overrides? - ) do - struct!(type, deep_merge(Map.from_struct(left), Map.from_struct(right))) - end - - defp deep_resolve(_key, %{__struct__: type}, _right, _concat_lists?, _struct_overrides?) do - raise ArgumentError, "#{type} cannot be merged with non-#{type}." - end - - defp deep_resolve(_key, _left, %{__struct__: type}, _concat_lists?, _struct_overrides?) do - raise ArgumentError, "Non-#{type} cannot be merged with #{type}." - end - - defp deep_resolve(_key, %{} = left, %{} = right, _concat_lists?, _struct_overrides?) do - deep_merge(left, right) - end - - defp deep_resolve(_key, left, right, true, _struct_overrides?) - when is_list(left) and is_list(right) do - left ++ right - end - - defp deep_resolve(_key, _left, right, _concat_lists?, _struct_overrides?), do: right -end diff --git a/mix.exs b/mix.exs index 41a76da..43d4e53 100644 --- a/mix.exs +++ b/mix.exs @@ -1,12 +1,12 @@ -defmodule Refinery.MixProject do +defmodule Refactory.MixProject do use Mix.Project - @source_url "https://github.com/infer-beam/refinery" + @source_url "https://github.com/elixir-dx/refactory" @version "0.1.0" def project do [ - app: :refinery, + app: :refactory, version: @version, elixir: "~> 1.10", start_permanent: Mix.env() == :prod, @@ -20,12 +20,12 @@ defmodule Refinery.MixProject do defp package do [ - description: "Generate test data recursively with refinements", + description: "Generate test data recursively with traits", files: ["lib", "mix.exs", "README*"], maintainers: ["Arno Dirlam"], licenses: ["MIT"], links: %{ - Changelog: "https://github.com/infer-beam/refinery/blob/main/CHANGELOG.md", + Changelog: "https://github.com/elixir-dx/refactory/blob/main/CHANGELOG.md", GitHub: @source_url } ] @@ -60,7 +60,7 @@ defmodule Refinery.MixProject do def docs do [ - main: "Refinery", + main: "Refactory", source_url: @source_url, source_ref: "v#{@version}" ] diff --git a/test/refinery/assignments_test.exs b/test/refactory/assignments_test.exs similarity index 89% rename from test/refinery/assignments_test.exs rename to test/refactory/assignments_test.exs index f182a93..95bb1e6 100644 --- a/test/refinery/assignments_test.exs +++ b/test/refactory/assignments_test.exs @@ -1,22 +1,22 @@ -defmodule Refinery.AssignmentsTest do - use Refinery.Test.DataCase +defmodule Refactory.AssignmentsTest do + use Refactory.Test.DataCase defmodule Factories do - use Refinery, repo: Refinery.Test.Repo + use Refactory, repo: Refactory.Test.Repo - def refinement(List, :default) do + def trait(List, :default) do %{ title: Enum.random(~w[Learning Professional Travel]) } end - def refinement(List, :with_user) do + def trait(List, :with_user) do %{ created_by: %{} } end - def refinement(ListTag, :default) do + def trait(ListTag, :default) do %{ name: Enum.random(~w[easy medium hard]), list: %{ @@ -25,7 +25,7 @@ defmodule Refinery.AssignmentsTest do } end - def refinement(User, :default) do + def trait(User, :default) do %{ email: "default@email.org" } diff --git a/test/refinery/defaults_test.exs b/test/refactory/defaults_test.exs similarity index 92% rename from test/refinery/defaults_test.exs rename to test/refactory/defaults_test.exs index acfe8f0..da070ea 100644 --- a/test/refinery/defaults_test.exs +++ b/test/refactory/defaults_test.exs @@ -1,22 +1,22 @@ -defmodule Refinery.DefaultsTest do - use Refinery.Test.DataCase +defmodule Refactory.DefaultsTest do + use Refactory.Test.DataCase defmodule Factories do - use Refinery, repo: Refinery.Test.Repo + use Refactory, repo: Refactory.Test.Repo - def refinement(List, :default) do + def trait(List, :default) do %{ title: Enum.random(~w[Learning Professional Travel]) } end - def refinement(ListTag, :default) do + def trait(ListTag, :default) do %{ name: Enum.random(~w[easy medium hard]) } end - def refinement(User, :default) do + def trait(User, :default) do %{ email: "default@email.org" } diff --git a/test/refinery/plain_test.exs b/test/refactory/plain_test.exs similarity index 96% rename from test/refinery/plain_test.exs rename to test/refactory/plain_test.exs index cd3eb73..b8c1006 100644 --- a/test/refinery/plain_test.exs +++ b/test/refactory/plain_test.exs @@ -1,8 +1,8 @@ -defmodule Refinery.PlainTest do - use Refinery.Test.DataCase +defmodule Refactory.PlainTest do + use Refactory.Test.DataCase defmodule Factories do - use Refinery, repo: Refinery.Test.Repo + use Refactory, repo: Refactory.Test.Repo end test "builds simple record" do diff --git a/test/refactory/refinements_test.exs b/test/refactory/refinements_test.exs new file mode 100644 index 0000000..e962e45 --- /dev/null +++ b/test/refactory/refinements_test.exs @@ -0,0 +1,150 @@ +defmodule Test.Support.RefinementsTest do + use Refactory.Test.DataCase, async: true + + defmodule Traits do + use Refactory, repo: Refactory.Test.Repo + + def trait(List, :default) do + %{ + title: Enum.random(~w[Learning Professional Travel]) + } + end + + def trait(ListTag, :default) do + %{ + name: Enum.random(~w[easy medium hard]) + } + end + + def trait(User, :default) do + %{ + email: "default@email.org", + last_name: "Medina" + } + end + + def trait(User, :refined) do + %{ + email: "refined@email.org" + } + end + end + + test "works with normal overrides" do + now = DateTime.utc_now() + list = Traits.build(List, %{archived_at: now}) + + assert %List{ + archived_at: ^now, + from_template: %Ecto.Association.NotLoaded{} + } = list + end + + test "works with nested overrides" do + list_tag = Traits.build(ListTag, %{list: %{created_by: %{last_name: "Vega"}}}) + + assert %ListTag{ + list: %List{ + created_by: %User{ + last_name: "Vega" + } + } + } = list_tag + end + + test "works with struct override" do + user = Traits.build(User) + + record = Traits.build(ListTag, %{list: %{created_by: user}}) + + assert %ListTag{ + list: %List{ + created_by: ^user + } + } = record + end + + test "raises on invalid struct override" do + user = Traits.build(User) + + assert_raise ArgumentError, fn -> + Traits.build(ListTag, %{list: user}) + end + end + + test "works with simple trait" do + list_tag = Traits.build(ListTag, %{list: %{created_by: :refined}}) + + assert %ListTag{ + list: %List{ + created_by: %User{ + email: "refined@email.org", + last_name: "Medina" + } + } + } = list_tag + end + + # test "works with database lookup trait 1" do + # department = get_dept_by(name: "Construction") + + # record = + # Traits.build( + # Timecards.TimecardData, + # %{timecard: %{offer: {:department_name, "Construction"}}} + # ) + + # assert %Timecards.TimecardData{ + # timecard: %Timecards.Timecard{ + # offer: %Production.Offer{ + # department: ^department + # } + # } + # } = record + # end + + # test "works with database lookup trait 2" do + # department = get_dept_by(name: "Construction") + + # record = + # Traits.build( + # Timecards.TimecardData, + # %{timecard: %{offer: %{department: {:name, "Construction"}}}} + # ) + + # assert %Timecards.TimecardData{ + # timecard: %Timecards.Timecard{ + # offer: %Production.Offer{ + # department: ^department + # } + # } + # } = record + # end + + test "raises on unknown trait" do + assert_raise ArgumentError, fn -> + Traits.build(List, %{created_by: :unknown}) + end + end + + test "works with trait + override" do + record = + Traits.build( + ListTag, + %{ + list: %{ + created_by: {:refined, %{last_name: "Vega"}} + } + } + ) + + assert %ListTag{ + list: %List{ + created_by: %User{ + email: "refined@email.org", + last_name: "Vega" + } + } + } = record + end +end diff --git a/test/refinery/refinements_test.exs b/test/refinery/refinements_test.exs deleted file mode 100644 index 7ace54c..0000000 --- a/test/refinery/refinements_test.exs +++ /dev/null @@ -1,114 +0,0 @@ -defmodule Test.Support.RefinementsTest do - use Refinery.Test.DataCase, async: true - - defmodule Refinements do - use Refinery, repo: Refinery.Test.Repo - - def refinement(List, :default) do - %{ - title: Enum.random(~w[Learning Professional Travel]) - } - end - - def refinement(ListTag, :default) do - %{ - name: Enum.random(~w[easy medium hard]) - } - end - - def refinement(User, :default) do - %{ - email: "default@email.org", - last_name: "Medina" - } - end - - def refinement(User, :refined) do - %{ - email: "refined@email.org" - } - end - end - - test "works with normal overrides" do - now = DateTime.utc_now() - list = Refinements.build(List, %{archived_at: now}) - - assert %List{ - archived_at: ^now, - from_template: %Ecto.Association.NotLoaded{} - } = list - end - - test "works with nested overrides" do - list_tag = Refinements.build(ListTag, %{list: %{created_by: %{last_name: "Vega"}}}) - - assert %ListTag{ - list: %List{ - created_by: %User{ - last_name: "Vega" - } - } - } = list_tag - end - - test "works with struct override" do - user = Refinements.build(User) - - record = Refinements.build(ListTag, %{list: %{created_by: user}}) - - assert %ListTag{ - list: %List{ - created_by: ^user - } - } = record - end - - test "raises on invalid struct override" do - user = Refinements.build(User) - - assert_raise ArgumentError, fn -> - Refinements.build(ListTag, %{list: user}) - end - end - - test "works with simple refinement" do - list_tag = Refinements.build(ListTag, %{list: %{created_by: :refined}}) - - assert %ListTag{ - list: %List{ - created_by: %User{ - email: "refined@email.org", - last_name: "Medina" - } - } - } = list_tag - end - - test "raises on unknown refinement" do - assert_raise ArgumentError, fn -> - Refinements.build(List, %{created_by: :unknown}) - end - end - - test "works with refinement + override" do - record = - Refinements.build( - ListTag, - %{ - list: %{ - created_by: {:refined, %{last_name: "Vega"}} - } - } - ) - - assert %ListTag{ - list: %List{ - created_by: %User{ - email: "refined@email.org", - last_name: "Vega" - } - } - } = record - end -end diff --git a/test/repo.ex b/test/repo.ex index 38c3009..5d37f11 100644 --- a/test/repo.ex +++ b/test/repo.ex @@ -1,5 +1,5 @@ -defmodule Refinery.Test.Repo do +defmodule Refactory.Test.Repo do use Ecto.Repo, - otp_app: :refinery, + otp_app: :refactory, adapter: Ecto.Adapters.Postgres end diff --git a/test/schema/list.ex b/test/schema/list.ex index d688b6e..6730c9c 100644 --- a/test/schema/list.ex +++ b/test/schema/list.ex @@ -1,7 +1,7 @@ -defmodule Refinery.Test.Schema.List do +defmodule Refactory.Test.Schema.List do use Ecto.Schema - alias Refinery.Test.Schema.{ListTemplate, Task, User} + alias Refactory.Test.Schema.{ListTemplate, Task, User} schema "lists" do field(:title, :string) diff --git a/test/schema/list_tag.ex b/test/schema/list_tag.ex index 9560519..7a82013 100644 --- a/test/schema/list_tag.ex +++ b/test/schema/list_tag.ex @@ -1,7 +1,7 @@ -defmodule Refinery.Test.Schema.ListTag do +defmodule Refactory.Test.Schema.ListTag do use Ecto.Schema - alias Refinery.Test.Schema.List + alias Refactory.Test.Schema.List schema "list_tags" do belongs_to(:list, List) diff --git a/test/schema/list_template.ex b/test/schema/list_template.ex index d59c299..6700b2f 100644 --- a/test/schema/list_template.ex +++ b/test/schema/list_template.ex @@ -1,7 +1,7 @@ -defmodule Refinery.Test.Schema.ListTemplate do +defmodule Refactory.Test.Schema.ListTemplate do use Ecto.Schema - alias Refinery.Test.Schema.List + alias Refactory.Test.Schema.List schema "list_templates" do field(:title, :string) diff --git a/test/schema/migrations/20220126160251_create_base_schema.exs b/test/schema/migrations/20220126160251_create_base_schema.exs index 65b16f2..03b8517 100644 --- a/test/schema/migrations/20220126160251_create_base_schema.exs +++ b/test/schema/migrations/20220126160251_create_base_schema.exs @@ -1,4 +1,4 @@ -defmodule Refinery.Test.Repo.Migrations.CreateBaseSchema do +defmodule Refactory.Test.Repo.Migrations.CreateBaseSchema do use Ecto.Migration def change do diff --git a/test/schema/task.ex b/test/schema/task.ex index f19fe4b..0af2094 100644 --- a/test/schema/task.ex +++ b/test/schema/task.ex @@ -1,7 +1,7 @@ -defmodule Refinery.Test.Schema.Task do +defmodule Refactory.Test.Schema.Task do use Ecto.Schema - alias Refinery.Test.Schema.{List, User} + alias Refactory.Test.Schema.{List, User} schema "tasks" do field(:title, :string) diff --git a/test/schema/user.ex b/test/schema/user.ex index d00df18..287565b 100644 --- a/test/schema/user.ex +++ b/test/schema/user.ex @@ -1,7 +1,7 @@ -defmodule Refinery.Test.Schema.User do +defmodule Refactory.Test.Schema.User do use Ecto.Schema - alias Refinery.Test.Schema.List + alias Refactory.Test.Schema.List schema "users" do field(:email, :string) diff --git a/test/support/data_case.ex b/test/support/data_case.ex index cc1e33c..5b5f4e4 100644 --- a/test/support/data_case.ex +++ b/test/support/data_case.ex @@ -1,4 +1,4 @@ -defmodule Refinery.Test.DataCase do +defmodule Refactory.Test.DataCase do @moduledoc """ This module defines the setup for tests requiring access to the application's data layer. @@ -16,16 +16,16 @@ defmodule Refinery.Test.DataCase do using do quote do - alias Refinery.Test.Repo - alias Refinery.Test.Schema.{List, ListTag, ListTemplate, Task, User} + alias Refactory.Test.Repo + alias Refactory.Test.Schema.{List, ListTag, ListTemplate, Task, User} end end setup tags do - :ok = Ecto.Adapters.SQL.Sandbox.checkout(Refinery.Test.Repo) + :ok = Ecto.Adapters.SQL.Sandbox.checkout(Refactory.Test.Repo) unless tags[:async] do - Ecto.Adapters.SQL.Sandbox.mode(Refinery.Test.Repo, {:shared, self()}) + Ecto.Adapters.SQL.Sandbox.mode(Refactory.Test.Repo, {:shared, self()}) end :ok diff --git a/test/test_helper.exs b/test/test_helper.exs index c15659b..ccda09b 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -2,7 +2,7 @@ if System.get_env("WARNINGS_AS_ERRORS") == "true" do Code.compiler_options(warnings_as_errors: true) end -{:ok, _} = Refinery.Test.Repo.start_link() -Ecto.Adapters.SQL.Sandbox.mode(Refinery.Test.Repo, :manual) +{:ok, _} = Refactory.Test.Repo.start_link() +Ecto.Adapters.SQL.Sandbox.mode(Refactory.Test.Repo, :manual) ExUnit.start()