Compare commits

...

73 commits
v0.7.0 ... main

Author SHA1 Message Date
a750c4a7ff chore(deps): update dependency ash to v3.4.6
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-09-07 02:23:32 +12:00
e77bd9de2a chore(deps): update dependency ash to v3.4.5
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-09-06 06:21:51 +12:00
39bd04b4af chore(deps): update dependency ash to v3.4.4
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-09-05 15:21:33 +12:00
13c5619376 chore(deps): update dependency ash to v3.4.3
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-09-05 03:19:43 +12:00
585766dd43 chore(deps): update dependency ash to v3.4.2
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-09-04 15:19:42 +12:00
3e18763805 chore(deps): update dependency spark to v2.2.23
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-08-30 10:20:57 +12:00
5e3aa634ec chore(deps): update dependency spark to v2.2.22
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2024-08-20 14:36:09 +12:00
90aad764f5
chore: unlock unused deps.
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-08-17 20:16:44 +12:00
b862125133
chore(deps): update dependency ash to v3.4.1 2024-08-17 20:16:34 +12:00
275110ba13
chore: fix failing test from Spark update
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-08-17 19:45:09 +12:00
ffc5407b2c chore(deps): update dependency spark to v2.2.21
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
renovate/artifacts Artifact file update failure
2024-08-15 22:11:29 +12:00
64d17a44c5 chore(deps): update dependency spark to v2.2.19
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-08-14 13:15:51 +12:00
3f17cd9e99 chore(deps): update dependency ash to v3.3.3
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-08 16:32:27 +12:00
3a0ddd207e chore(deps): update dependency spark to v2.2.11
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-08-08 16:25:37 +12:00
324e15ca46 chore(deps): update dependency ash to v3.3.0
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-28 09:29:45 +12:00
43f5360130
chore: remove unused dependencies.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2024-07-27 15:33:03 +12:00
43535e1d28 chore(deps): update dependency ash to v3.2.6
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2024-07-27 06:36:30 +12:00
3bbc2d62a3 chore(deps): update dependency mix_audit to v2.1.4
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-17 02:32:00 +12:00
975f7335e5 chore(deps): update dependency ash to v3.1.8
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-15 06:35:47 +12:00
e11660011e chore(deps): update dependency ash to v3.1.7
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-15 03:39:47 +12:00
01962eb4c9 chore(deps): update dependency ash to v3.1.5
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-15 01:36:14 +12:00
a217a0fe11 chore(deps): update dependency ash to v3.1.4
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2024-07-14 08:36:44 +12:00
60be63d5d2 chore(deps): update dependency earmark to v1.4.47
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-13 06:39:02 +12:00
8c4e71614d chore(deps): update dependency ash to v3.1.3
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-12 03:41:39 +12:00
e6df6910c7 chore(deps): update dependency ash to v3.1.2
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-11 04:35:20 +12:00
f767b723e0 chore(deps): update dependency erlang to v27.0.1
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-11 02:55:57 +12:00
df1f1309e4 chore(deps): update dependency ash to v3.1.1
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-11 01:39:42 +12:00
7b041d3055 chore(deps): update dependency ash to v3.1.0
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-10 12:35:00 +12:00
334363db70 chore(deps): update dependency ex_doc to v0.34.2
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-09 03:43:11 +12:00
5d5edad3e5 chore(deps): update dependency elixir to v1.17.2
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-07 09:38:21 +12:00
e60facc798 docs: fix typo in related docs (#110)
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #110
Co-authored-by: James Harton <james@harton.nz>
Co-committed-by: James Harton <james@harton.nz>
2024-07-04 10:29:21 +12:00
8cc0de70c4 chore(deps): update dependency spark to v2.2.7
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-07-03 10:35:55 +12:00
3eb46cb485 chore(deps): update dependency spark to v2.2.6
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-29 10:39:44 +12:00
452f827067 chore(deps): update dependency spark to v2.2.5
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-26 07:37:37 +12:00
e590e8d9e8 chore(deps): update dependency recase to v0.8.1
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-25 18:37:11 +12:00
e074b261d2 chore(deps): update dependency ash to v3.0.16
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-22 07:18:53 +12:00
fbabaa6c93 chore(deps): update dependency spark to v2.2.4
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-21 06:20:17 +12:00
59ea39c8ab chore(deps): update dependency ex_doc to v0.34.1
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-21 02:21:25 +12:00
e18319f525 chore(deps): update dependency elixir to v1.17.1
Some checks reported errors
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build encountered an error
2024-06-19 12:23:17 +12:00
957e2845c7 chore(deps): update dependency ash to v3.0.15
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-19 11:23:38 +12:00
00cdf8106d chore(deps): update dependency ash to v3.0.14
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-19 04:21:51 +12:00
d756b1ac12 chore(deps): update dependency spark to v2.2.3
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-18 10:21:11 +12:00
e8fb1bb305 chore(deps): update dependency spark to v2.2.2
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-18 06:19:26 +12:00
8dd043f13a chore(deps): update dependency ash to v3.0.13
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-18 04:18:45 +12:00
9a26e7a74b chore(deps): update dependency elixir to v1.17.0
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-15 13:43:13 +12:00
ccbe9a124f chore(deps): update dependency credo to v1.7.7
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-15 13:27:13 +12:00
5d767a2e91 chore(deps): update dependency ash to v3.0.12
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-15 12:52:01 +12:00
13bd3c81b7 chore(deps): update dependency ash to v3.0.11
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-12 03:22:20 +12:00
a874167d10 chore(deps): update dependency spark to v2.1.24
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-11 03:19:53 +12:00
81b312b172 chore(deps): update dependency spark to v2.1.23
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-08 02:22:07 +12:00
f9dc1112f0 chore(deps): update dependency ash to v3.0.10
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-07 06:33:11 +12:00
c5adaf448b chore(deps): update dependency ash to v3.0.9
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-06-01 03:18:37 +12:00
85aa3e2f6e chore: release version v0.9.1
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2024-05-30 20:54:44 +00:00
f8480ec8e7 fix: allow auto builds to be replaced by explicit relate commands.
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-31 08:54:03 +12:00
Rebecca Le
d5be51556d bug: Add failing test for auto-build and relate options colliding 2024-05-31 08:54:03 +12:00
ebc07800b4 chore(deps): update dependency ex_doc to v0.34.0
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-05-31 01:24:03 +12:00
5368a030dd chore: release version v0.9.0
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2024-05-28 21:51:27 +00:00
ef5d6462b9 feat: Auto build/load factory options. (#83)
All checks were successful
continuous-integration/drone/push Build is passing
New factory DSL options:

1. `auto_build` allows you to provide a list of relationships which must also be built when building that factory.
2. `auto_load` allows you to provide a load statement for relationships and calculations that must be loaded when building that factory.

Reviewed-on: #83
Co-authored-by: James Harton <james@harton.nz>
Co-committed-by: James Harton <james@harton.nz>
2024-05-29 09:50:43 +12:00
f46d9bb6b9 chore(deps): update dependency ash to v3.0.8
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-05-29 07:19:14 +12:00
4786ee97e6 chore(deps): update dependency recase to ~> 0.8
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-05-28 21:22:06 +12:00
0d42238d6f chore: release version v0.8.1
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2024-05-28 04:48:48 +00:00
cb2d0376b5
fix: Include :variant in option schema.
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
This required a bit of a rework of how the options are validated.  Now they're only validated once when `Builder.build` is called instead of inside each builder.
2024-05-28 16:40:43 +12:00
aa65f4912b
fix: bug with generator arguments. 2024-05-28 16:33:00 +12:00
5c524faef8 chore: release version v0.8.0
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2024-05-26 08:51:36 +00:00
5f8e19ee07
feat: Add before_build and after_build entities to factories.
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-26 20:50:33 +12:00
56e6fb9a4c chore(deps): update dependency ash to v3.0.7
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-05-24 18:18:27 +12:00
04f1393c7c chore(deps): update dependency ash to v3.0.6
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-05-24 11:19:07 +12:00
bab42763d3 chore(deps): update dependency ash to v3.0.5
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-05-24 03:17:37 +12:00
1ed4612aec chore(deps): update dependency ash to v3.0.4
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-05-23 11:19:09 +12:00
9a5579bb50 chore(deps): update dependency ash to v3.0.3
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-05-23 10:19:03 +12:00
f03b4487bf chore(deps): update dependency ex_doc to v0.33.0
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-05-22 01:20:12 +12:00
535acaa1a2 chore(deps): update dependency elixir to v1.16.3
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-05-21 11:21:16 +12:00
550178ed34 chore(deps): update dependency erlang to v27
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-05-20 21:42:45 +12:00
28 changed files with 668 additions and 110 deletions

View file

@ -1,6 +1,12 @@
spark_locals_without_parens = [
after_build: 1,
after_build: 2,
attribute: 2,
attribute: 3,
auto_build: 1,
auto_load: 1,
before_build: 1,
before_build: 2,
domain: 1,
factory: 1,
factory: 2,

View file

@ -1,2 +1,2 @@
elixir 1.16.2-otp-26
erlang 26.2.5
elixir 1.17.2
erlang 27.0.1

8
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,8 @@
{
"cSpell.words": [
"arities",
"Cardassia",
"recase",
"Slickback"
]
}

View file

@ -5,6 +5,44 @@ See [Conventional Commits](Https://conventionalcommits.org) for commit guideline
<!-- changelog -->
## [v0.9.1](https://harton.dev/james/smokestack/compare/v0.9.0...v0.9.1) (2024-05-30)
### Bug Fixes:
* allow auto builds to be replaced by explicit relate commands.
## [v0.9.0](https://harton.dev/james/smokestack/compare/v0.8.1...v0.9.0) (2024-05-28)
### Features:
* Auto build/load factory options. (#83)
## [v0.8.1](https://harton.dev/james/smokestack/compare/v0.8.0...v0.8.1) (2024-05-28)
### Bug Fixes:
* Include `:variant` in option schema.
* bug with generator arguments.
## [v0.8.0](https://harton.dev/james/smokestack/compare/v0.7.0...v0.8.0) (2024-05-26)
### Features:
* Add `before_build` and `after_build` entities to factories.
## [v0.7.0](https://harton.dev/james/smokestack/compare/v0.6.2...v0.7.0) (2024-05-20)

View file

@ -38,7 +38,7 @@ add it directly to your `mix.exs`:
```elixir
def deps do
[
{:smokestack, "~> 0.7.0"},
{:smokestack, "~> 0.9.1"},
]
end
```

View file

@ -13,7 +13,9 @@ The DSL definition for the Smokestack DSL.
* smokestack
* factory
* after_build
* attribute
* before_build
### Docs
@ -22,7 +24,9 @@ The DSL definition for the Smokestack DSL.
* [factory](#module-factory)
* after_build
* attribute
* before_build
@ -38,7 +42,9 @@ The DSL definition for the Smokestack DSL.
Define factories for a resource
* after_build
* attribute
* before_build
@ -48,6 +54,30 @@ Define factories for a resource
* `:variant` (`t:atom/0`) - The name of a factory variant The default value is `:default`.
* `:auto_build` (one or a list of `t:atom/0`) - A list of relationships that should always be built when building this factory The default value is `[]`.
* `:auto_load` - An Ash "load statement" to always apply when building this factory The default value is `[]`.
##### after_build
Modify the record after building.
Allows you to provide a function which can modify the built record before returning.
These hooks are only applied when building records and not parameters.
* `:hook` (mfa or function of arity 1) - Required. A function which returns an updated record
##### attribute
@ -67,6 +97,24 @@ Define factories for a resource
##### before_build
Modify the attributes before building.
Allows you to provide a function which can modify the the attributes before building.
* `:hook` (mfa or function of arity 1) - Required. A function which returns an updated record
@ -79,7 +127,9 @@ Define factories for a resource
### Nested DSLs
* [factory](#smokestack-factory)
* after_build
* attribute
* before_build
@ -102,7 +152,9 @@ factory resource, variant \\ :default
Define factories for a resource
### Nested DSLs
* [after_build](#smokestack-factory-after_build)
* [attribute](#smokestack-factory-attribute)
* [before_build](#smokestack-factory-before_build)
@ -118,8 +170,42 @@ Define factories for a resource
| Name | Type | Default | Docs |
|------|------|---------|------|
| [`domain`](#smokestack-factory-domain){: #smokestack-factory-domain } | `module` | | The Ash Domain to use when evaluating loads |
| [`auto_build`](#smokestack-factory-auto_build){: #smokestack-factory-auto_build } | `atom \| list(atom)` | `[]` | A list of relationships that should always be built when building this factory |
| [`auto_load`](#smokestack-factory-auto_load){: #smokestack-factory-auto_load } | `atom \| keyword \| list(atom \| keyword)` | `[]` | An Ash "load statement" to always apply when building this factory |
## smokestack.factory.after_build
```elixir
after_build hook
```
Modify the record after building.
Allows you to provide a function which can modify the built record before returning.
These hooks are only applied when building records and not parameters.
### Arguments
| Name | Type | Default | Docs |
|------|------|---------|------|
| [`hook`](#smokestack-factory-after_build-hook){: #smokestack-factory-after_build-hook .spark-required} | `(any -> any) \| mfa` | | A function which returns an updated record |
### Introspection
Target: `Smokestack.Dsl.AfterBuild`
## smokestack.factory.attribute
```elixir
attribute name, generator
@ -148,6 +234,36 @@ attribute name, generator
Target: `Smokestack.Dsl.Attribute`
## smokestack.factory.before_build
```elixir
before_build hook
```
Modify the attributes before building.
Allows you to provide a function which can modify the the attributes before building.
### Arguments
| Name | Type | Default | Docs |
|------|------|---------|------|
| [`hook`](#smokestack-factory-before_build-hook){: #smokestack-factory-before_build-hook .spark-required} | `(any -> any) \| mfa` | | A function which returns an updated record |
### Introspection
Target: `Smokestack.Dsl.BeforeBuild`

View file

@ -112,7 +112,7 @@ defmodule Smokestack do
use Dsl, default_extensions: [extensions: [Smokestack.Dsl]]
alias Ash.Resource
alias Smokestack.{Builder, Dsl.Info, ParamBuilder, RecordBuilder}
alias Smokestack.{Builder, ParamBuilder, RecordBuilder}
@type t :: module
@ -196,11 +196,7 @@ defmodule Smokestack do
@spec params(Resource.t(), [Smokestack.param_option()]) ::
{:ok, ParamBuilder.result()} | {:error, any}
def params(resource, options \\ []) do
{variant, options} = Keyword.pop(options, :variant, :default)
with {:ok, factory} <- Info.factory(__MODULE__, resource, variant) do
Builder.build(ParamBuilder, factory, options)
end
Builder.build(__MODULE__, resource, ParamBuilder, options)
end
@doc """
@ -229,11 +225,7 @@ defmodule Smokestack do
@spec insert(Resource.t(), [Smokestack.insert_option()]) ::
{:ok, RecordBuilder.result()} | {:error, any}
def insert(resource, options \\ []) do
{variant, options} = Keyword.pop(options, :variant, :default)
with {:ok, factory} <- Info.factory(__MODULE__, resource, variant) do
Builder.build(RecordBuilder, factory, options)
end
Builder.build(__MODULE__, resource, RecordBuilder, options)
end
@doc """

View file

@ -3,7 +3,8 @@ defmodule Smokestack.Builder do
A generic behaviour for "building things".
"""
alias Smokestack.Dsl.Factory
alias Ash.Resource
alias Smokestack.Dsl.{Factory, Info}
alias Spark.Options
@type result :: any
@ -18,14 +19,18 @@ defmodule Smokestack.Builder do
@doc """
Provide a schema for validating options.
"""
@callback option_schema(nil | Factory.t()) :: {:ok, Options.schema()} | {:error, any}
@callback option_schema(nil | Factory.t()) ::
{:ok, Options.schema(), String.t()} | {:error, any}
@doc """
Given a builder and a factory, validate it's options and call the builder.
Find the appropriate factory, validate options and run the builder.
"""
@spec build(t, Factory.t(), Keyword.t()) :: {:ok, result} | {:error, error}
def build(builder, factory, options) do
with {:ok, schema} <- builder.option_schema(factory),
@spec build(Smokestack.t(), Resource.t(), t, Keyword.t()) :: {:ok, result} | {:error, error}
def build(factory_module, resource, builder, options) do
with {:ok, our_schema} <- variant_schema(factory_module, resource),
{:ok, factory} <- Info.factory(factory_module, resource, options[:variant] || :default),
{:ok, builder_schema, section} <- builder.option_schema(factory),
schema <- Options.merge(our_schema, builder_schema, section),
{:ok, options} <- Options.validate(options, schema) do
builder.build(factory, options)
end
@ -36,7 +41,29 @@ defmodule Smokestack.Builder do
"""
@spec docs(t, nil | Factory.t()) :: String.t()
def docs(builder, factory) do
{:ok, schema} = builder.option_schema(factory)
{:ok, schema, _} = builder.option_schema(factory)
Options.docs(schema)
end
defp variant_schema(factory_module, resource) do
case Info.variants(factory_module, resource) do
[] ->
{:error,
"There are no factories defined for the resource `#{inspect(resource)}` in the `#{inspect(factory_module)}` module."}
variants ->
our_schema = [
variant: [
type: {:in, variants},
required: false,
default: :default,
doc: """
The name of the factory variant to use.
"""
]
]
{:ok, our_schema}
end
end
end

View file

@ -27,19 +27,28 @@ defmodule Smokestack.FactoryBuilder do
overrides = Keyword.get(options, :attrs, %{})
with {:ok, overrides} <- validate_overrides(factory, overrides) do
factory.attributes
|> remove_overridden_attrs(overrides)
|> Enum.reduce({:ok, overrides}, fn attr, {:ok, attrs} ->
generator = maybe_initialise_generator(attr)
value = Template.generate(generator, attr, options)
{:ok, Map.put(attrs, attr.name, value)}
end)
attrs =
factory.attributes
|> remove_overridden_attrs(overrides)
|> Enum.reduce(overrides, fn attr, attrs ->
generator = maybe_initialise_generator(attr)
value = Template.generate(generator, attrs, options)
Map.put(attrs, attr.name, value)
end)
attrs =
factory.before_build
|> Enum.reduce(attrs, fn hook, attrs ->
hook.hook.(attrs)
end)
{:ok, attrs}
end
end
@doc false
@impl true
@spec option_schema(nil | Factory.t()) :: {:ok, Options.schema()} | {:error, error}
@spec option_schema(nil | Factory.t()) :: {:ok, Options.schema(), String.t()} | {:error, error}
def option_schema(factory) do
attr_keys =
if factory do
@ -71,7 +80,7 @@ defmodule Smokestack.FactoryBuilder do
```
"""
]
]}
], "Options for building instances"}
end
defp maybe_initialise_generator(attr) do

View file

@ -30,9 +30,9 @@ defmodule Smokestack.ManyBuilder do
@doc false
@impl true
@spec option_schema(nil | Factory.t()) :: {:ok, Options.schema()} | {:error, error}
@spec option_schema(nil | Factory.t()) :: {:ok, Options.schema(), String.t()} | {:error, error}
def option_schema(factory) do
with {:ok, related_schema} <- RelatedBuilder.option_schema(factory) do
with {:ok, related_schema, related_section} <- RelatedBuilder.option_schema(factory) do
schema =
[
count: [
@ -53,16 +53,16 @@ defmodule Smokestack.ManyBuilder do
"""
]
]
|> Options.merge(related_schema, "Options for building relationships")
|> Options.merge(related_schema, related_section)
{:ok, schema}
{:ok, schema, "Options for building multiple instances"}
end
end
defp do_build(factory, how_many, options) when how_many > 0 and is_integer(how_many) do
1..how_many
|> Enum.reduce_while({:ok, []}, fn _, {:ok, results} ->
case Builder.build(RelatedBuilder, factory, options) do
case RelatedBuilder.build(factory, options) do
{:ok, attrs} -> {:cont, {:ok, [attrs | results]}}
{:error, reason} -> {:halt, {:error, reason}}
end

View file

@ -69,10 +69,10 @@ defmodule Smokestack.ParamBuilder do
@doc false
@impl true
@spec option_schema(nil | Factory.t()) :: {:ok, Options.schema()} | {:error, error}
@spec option_schema(nil | Factory.t()) :: {:ok, Options.schema(), String.t()} | {:error, error}
def option_schema(factory) do
with {:ok, related_schema} <- RelatedBuilder.option_schema(factory),
{:ok, many_schema} <- ManyBuilder.option_schema(factory) do
with {:ok, related_schema, related_section} <- RelatedBuilder.option_schema(factory),
{:ok, many_schema, many_section} <- ManyBuilder.option_schema(factory) do
many_schema =
Keyword.update!(many_schema, :count, fn current ->
current
@ -178,10 +178,10 @@ defmodule Smokestack.ParamBuilder do
schema =
our_schema
|> Options.merge(many_schema, "Options for building multiple instances")
|> Options.merge(related_schema, "Options for building relationships")
|> Options.merge(many_schema, many_section)
|> Options.merge(related_schema, related_section)
{:ok, schema}
{:ok, schema, "Options for building parameters"}
end
end
@ -189,7 +189,7 @@ defmodule Smokestack.ParamBuilder do
{my_opts, their_opts} = split_options(options)
their_opts = Keyword.put(their_opts, :count, count)
with {:ok, attr_list} <- Builder.build(ManyBuilder, factory, their_opts) do
with {:ok, attr_list} <- ManyBuilder.build(factory, their_opts) do
attr_list
|> convert_keys(my_opts)
|> maybe_nest_result(my_opts[:nest])
@ -200,7 +200,7 @@ defmodule Smokestack.ParamBuilder do
defp do_build(factory, options, _) do
{my_opts, their_opts} = split_options(options)
with {:ok, attrs} <- Builder.build(RelatedBuilder, factory, their_opts) do
with {:ok, attrs} <- RelatedBuilder.build(factory, their_opts) do
attrs
|> convert_keys(my_opts)
|> maybe_nest_result(my_opts[:nest])

View file

@ -31,10 +31,10 @@ defmodule Smokestack.RecordBuilder do
@doc false
@impl true
@spec option_schema(nil | Factory.t()) :: {:ok, Options.schema()} | {:error, error}
@spec option_schema(nil | Factory.t()) :: {:ok, Options.schema(), String.t()} | {:error, error}
def option_schema(factory) do
with {:ok, related_schema} <- RelatedBuilder.option_schema(factory),
{:ok, many_schema} <- ManyBuilder.option_schema(factory) do
with {:ok, related_schema, related_section} <- RelatedBuilder.option_schema(factory),
{:ok, many_schema, many_section} <- ManyBuilder.option_schema(factory) do
load_type =
if factory do
loadable_names =
@ -87,10 +87,10 @@ defmodule Smokestack.RecordBuilder do
"""
]
]
|> Options.merge(many_schema, "Options for building multiple instances")
|> Options.merge(related_schema, "Options for building relationships")
|> Options.merge(many_schema, many_section)
|> Options.merge(related_schema, related_section)
{:ok, schema}
{:ok, schema, "Options for building records"}
end
end
@ -98,18 +98,22 @@ defmodule Smokestack.RecordBuilder do
{load, options} = Keyword.pop(options, :load, [])
options = Keyword.put(options, :count, count)
with {:ok, attr_list} <- Builder.build(ManyBuilder, factory, options),
with {:ok, attr_list} <- ManyBuilder.build(factory, options),
{:ok, record_list} <- seed(attr_list, factory) do
maybe_load(record_list, factory, load)
record_list
|> maybe_hook(factory)
|> maybe_load(factory, List.wrap(load))
end
end
defp do_build(factory, options, _count) do
{load, options} = Keyword.pop(options, :load, [])
with {:ok, attrs} <- Builder.build(RelatedBuilder, factory, options),
with {:ok, attrs} <- RelatedBuilder.build(factory, options),
{:ok, record} <- seed(attrs, factory) do
maybe_load(record, factory, load)
record
|> maybe_hook(factory)
|> maybe_load(factory, List.wrap(load))
end
end
@ -141,11 +145,30 @@ defmodule Smokestack.RecordBuilder do
|> Resource.put_metadata(:variant, factory.variant)
end
defp maybe_load(record_or_records, _factory, []), do: {:ok, record_or_records}
defp maybe_load(record_or_records, %{auto_load: []}, []), do: {:ok, record_or_records}
defp maybe_load(_record_or_records, factory, _load) when is_nil(factory.domain),
do: {:error, "Unable to perform `load` operation without an Domain."}
defp maybe_load(record_or_records, factory, load),
do: Ash.load(record_or_records, load, domain: factory.domain)
defp maybe_load(record_or_records, factory, load) do
load =
factory.auto_load
|> Enum.concat(load)
Ash.load(record_or_records, load, domain: factory.domain)
end
defp maybe_hook(records, factory) when is_list(records) do
Enum.map(records, fn record ->
Enum.reduce(factory.after_build, record, fn hook, record ->
hook.(record)
end)
end)
end
defp maybe_hook(record, factory) when is_map(record) do
Enum.reduce(factory.after_build, record, fn hook, record ->
hook.hook.(record)
end)
end
end

View file

@ -30,8 +30,7 @@ defmodule Smokestack.RelatedBuilder do
@impl true
@spec build(Factory.t(), [option]) :: {:ok, result} | {:error, error}
def build(factory, options) do
with {:ok, attrs} <-
Builder.build(FactoryBuilder, factory, Keyword.drop(options, [:build, :relate])),
with {:ok, attrs} <- FactoryBuilder.build(factory, Keyword.drop(options, [:build, :relate])),
{:ok, attrs} <- maybe_build_related(factory, attrs, options) do
maybe_relate(factory, attrs, options)
end
@ -39,9 +38,9 @@ defmodule Smokestack.RelatedBuilder do
@doc false
@impl true
@spec option_schema(nil | Factory.t()) :: {:ok, Options.schema()} | {:error, error}
@spec option_schema(nil | Factory.t()) :: {:ok, Options.schema(), String.t()} | {:error, error}
def option_schema(factory) do
with {:ok, factory_schema} <- FactoryBuilder.option_schema(factory) do
with {:ok, factory_schema, factory_section} <- FactoryBuilder.option_schema(factory) do
build_type =
if factory do
relationship_names =
@ -131,14 +130,14 @@ defmodule Smokestack.RelatedBuilder do
```elixir
author = insert!(Author)
post = insert!(Post, relate: [author: Author])
post = insert!(Post, relate: [author: author])
```
"""
]
]
|> Options.merge(factory_schema, "Options for building instances")
|> Options.merge(factory_schema, factory_section)
{:ok, schema}
{:ok, schema, "Options for building relationships"}
end
end
@ -146,10 +145,12 @@ defmodule Smokestack.RelatedBuilder do
options
|> Keyword.get(:build, [])
|> List.wrap()
|> Enum.concat(factory.auto_build)
|> Enum.map(fn
{key, value} -> {key, value}
key when is_atom(key) -> {key, []}
end)
|> remove_explicit_relates(options)
|> Enum.reduce_while({:ok, attrs}, fn {relationship, nested_builds}, {:ok, attrs} ->
case build_related(
attrs,
@ -163,6 +164,18 @@ defmodule Smokestack.RelatedBuilder do
end)
end
defp remove_explicit_relates(builds, options) do
relates =
options[:relate]
|> List.wrap()
|> Map.new()
builds
|> Enum.reject(fn {key, _value} ->
is_map_key(relates, key)
end)
end
defp build_related(attrs, relationship, factory, options) do
ash_relationship = Resource.Info.relationship(factory.resource, relationship)
build_related(attrs, relationship, factory, options, ash_relationship)
@ -182,8 +195,7 @@ defmodule Smokestack.RelatedBuilder do
|> Keyword.put(:attrs, %{})
with {:ok, related_factory} <- find_related_factory(relationship.destination, factory),
{:ok, related_attrs} <-
Builder.build(__MODULE__, related_factory, related_options) do
{:ok, related_attrs} <- __MODULE__.build(related_factory, related_options) do
case relationship.cardinality do
:one ->
{:ok, Map.put(attrs, relationship.name, related_attrs)}

View file

@ -0,0 +1,43 @@
defmodule Smokestack.Dsl.AfterBuild do
@moduledoc """
The `after_build` DSL entity.
See `d:Smokestack.factory.after_build` for more information.
"""
defstruct __identifier__: nil, hook: nil
alias Ash.Resource
alias Spark.Dsl.Entity
@type t :: %__MODULE__{
__identifier__: any,
hook: mfa | (Resource.record() -> Resource.record())
}
@doc false
@spec __entities__ :: [Entity.t()]
def __entities__,
do: [
%Entity{
name: :after_build,
describe: """
Modify the record after building.
Allows you to provide a function which can modify the built record before returning.
These hooks are only applied when building records and not parameters.
""",
target: __MODULE__,
args: [:hook],
identifier: {:auto, :unique_integer},
schema: [
hook: [
type: {:mfa_or_fun, 1},
required: true,
doc: "A function which returns an updated record"
]
]
}
]
end

View file

@ -2,7 +2,7 @@ defmodule Smokestack.Dsl.Attribute do
@moduledoc """
The `attribute ` DSL entity.
See `d:Smokestack.factory.default.attribute` for more information.
See `d:Smokestack.factory.attribute` for more information.
"""
defstruct __identifier__: nil, generator: nil, name: nil
@ -11,7 +11,7 @@ defmodule Smokestack.Dsl.Attribute do
alias Spark.Dsl.Entity
@type t :: %__MODULE__{
__identifier__: nil,
__identifier__: any,
generator:
mfa | (-> any) | (Resource.record() -> any) | (Resource.record(), keyword -> any),
name: atom

View file

@ -0,0 +1,41 @@
defmodule Smokestack.Dsl.BeforeBuild do
@moduledoc """
The `before_build` DSL entity.
See `d:Smokestack.factory.before_build` for more information.
"""
defstruct __identifier__: nil, hook: nil
alias Spark.Dsl.Entity
@type attrs :: %{required(String.t() | atom) => any}
@type t :: %__MODULE__{
__identifier__: any,
hook: mfa | (attrs -> attrs)
}
@doc false
@spec __entities__ :: [Entity.t()]
def __entities__,
do: [
%Entity{
name: :before_build,
describe: """
Modify the attributes before building.
Allows you to provide a function which can modify the the attributes before building.
""",
target: __MODULE__,
args: [:hook],
identifier: {:auto, :unique_integer},
schema: [
hook: [
type: {:mfa_or_fun, 1},
required: true,
doc: "A function which returns an updated record"
]
]
}
]
end

View file

@ -6,19 +6,27 @@ defmodule Smokestack.Dsl.Factory do
"""
defstruct __identifier__: nil,
after_build: [],
attributes: [],
auto_load: [],
auto_build: [],
before_build: [],
domain: nil,
module: nil,
resource: nil,
variant: :default
alias Ash.Resource
alias Smokestack.Dsl.{Attribute, Template}
alias Smokestack.Dsl.{AfterBuild, Attribute, BeforeBuild, Template}
alias Spark.Dsl.Entity
@type t :: %__MODULE__{
__identifier__: any,
after_build: [AfterBuild.t()],
attributes: [Attribute.t()],
auto_load: [atom] | Keyword.t(),
auto_build: [atom],
before_build: [BeforeBuild.t()],
domain: nil,
module: module,
resource: Resource.t(),
@ -52,9 +60,25 @@ defmodule Smokestack.Dsl.Factory do
required: false,
doc: "The name of a factory variant",
default: :default
],
auto_build: [
type: {:wrap_list, :atom},
required: false,
doc: "A list of relationships that should always be built when building this factory",
default: []
],
auto_load: [
type: {:wrap_list, {:or, [:atom, :keyword_list]}},
required: false,
doc: "An Ash \"load statement\" to always apply when building this factory",
default: []
]
],
entities: [attributes: Attribute.__entities__()]
entities: [
after_build: AfterBuild.__entities__(),
attributes: Attribute.__entities__(),
before_build: BeforeBuild.__entities__()
]
}
]
end

View file

@ -35,4 +35,16 @@ defmodule Smokestack.Dsl.Info do
{:error, reason} -> raise reason
end
end
@doc """
List all variants available for a resource.
"""
@spec variants(Smokestack.t(), Resource.t()) :: [atom]
def variants(factory, resource) do
factory
|> Extension.get_entities([:smokestack])
|> Enum.filter(&(is_struct(&1, Factory) && &1.resource == resource))
|> Enum.map(& &1.variant)
|> Enum.uniq()
end
end

View file

@ -9,7 +9,11 @@ defmodule Smokestack.Dsl.Verifier do
@impl true
@spec verify(Dsl.t()) :: :ok | {:error, DslError.t()}
def verify(dsl_state) do
error_info = %{module: Verifier.get_persisted(dsl_state, :module), path: [:smokestack]}
error_info = %{
module: Verifier.get_persisted(dsl_state, :module),
path: [:smokestack],
dsl_state: dsl_state
}
factories =
dsl_state
@ -68,7 +72,9 @@ defmodule Smokestack.Dsl.Verifier do
error_info =
Map.merge(error_info, %{resource: factory.resource, path: [:factory | error_info.path]})
with :ok <- verify_unique_attributes(factory, error_info) do
with :ok <- verify_unique_attributes(factory, error_info),
:ok <- verify_auto_build(factory, error_info),
:ok <- verify_auto_load(factory, error_info) do
factory
|> Map.get(:attributes, [])
|> Enum.filter(&is_struct(&1, Attribute))
@ -186,4 +192,76 @@ defmodule Smokestack.Dsl.Verifier do
)}
end
end
defp verify_auto_build(factory, error_info) do
error_info = %{error_info | path: [:auto_build | error_info.path]}
Enum.reduce_while(factory.auto_build, :ok, fn relationship, :ok ->
error_info = %{error_info | path: [relationship | error_info.path]}
with {:ok, relationship} <- verify_relationship(factory.resource, relationship, error_info),
:ok <- verify_factory_exists(relationship.destination, error_info) do
{:cont, :ok}
else
{:error, error} -> {:halt, {:error, error}}
end
end)
end
defp verify_relationship(resource, relationship, error_info) do
case Info.relationship(resource, relationship) do
nil ->
{:error,
DslError.exception(
module: error_info.module,
path: Enum.reverse(error_info.path),
message:
"The resource `#{inspect(resource)}` has no relationship named `#{inspect(relationship)}`."
)}
relationship ->
{:ok, relationship}
end
end
defp verify_factory_exists(resource, error_info) do
factory_exists? =
error_info.dsl_state
|> Verifier.get_entities([:smokestack])
|> Enum.any?(&(is_struct(&1, Factory) && &1.resource == resource))
if factory_exists? do
:ok
else
{:error,
DslError.exception(
module: error_info.module,
path: Enum.reverse(error_info.path),
message: "No factories defined for resource `#{inspect(resource)}`."
)}
end
end
defp verify_auto_load(factory, error_info) do
error_info = %{error_info | path: [:auto_load | error_info.path]}
Enum.reduce_while(factory.auto_load, :ok, fn load, :ok ->
error_info = %{error_info | path: [load | error_info.path]}
with nil <- Info.calculation(factory.resource, load),
nil <- Info.aggregate(factory.resource, load),
nil <- Info.relationship(factory.resource, load) do
{:halt,
{:error,
DslError.exception(
module: error_info.module,
path: Enum.reverse(error_info.path),
message:
"Expected an aggregate, calculation or relationship named `#{inspect(load)}` on resource `#{inspect(factory.resource)}`"
)}}
else
_ -> {:cont, :ok}
end
end)
end
end

View file

@ -1,7 +1,7 @@
defmodule Smokestack.MixProject do
use Mix.Project
@version "0.7.0"
@version "0.9.1"
@moduledoc """
Test factories for Ash resources.
@ -103,7 +103,7 @@ defmodule Smokestack.MixProject do
{:faker, "~> 0.18", opts},
{:git_ops, "~> 2.6", opts},
{:mix_audit, "~> 2.1", opts},
{:recase, "~> 0.7"},
{:recase, "~> 0.8"},
{:spark, "~> 2.1"}
]
end

View file

@ -1,37 +1,44 @@
%{
"ash": {:hex, :ash, "3.0.2", "2b3bec4c53a04ed8d415052cc992b6c76e0e348fa2fae06cdb28d6769709438c", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, ">= 0.8.1 and < 1.0.0-0", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.1.18 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "784b6a3ade4b0c8e5c16cd5792ab080affef8729d48e32c15e4e283c93696104"},
"ash": {:hex, :ash, "3.4.6", "3210de7f0dff06d30f334fb05da13929779d70d861f95443c88ac4edab5a4500", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, ">= 0.3.11 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 0.9", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.2.22 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4502b9cc192ac1e68cbbf7e7dd3de4180715190e320bc0a5d477af68d079f006"},
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"comparable": {:hex, :comparable, "1.0.0", "bb669e91cedd14ae9937053e5bcbc3c52bb2f22422611f43b6e38367d94a495f", [:mix], [{:typable, "~> 0.1", [hex: :typable, repo: "hexpm", optional: false]}], "hexpm", "277c11eeb1cd726e7cd41c6c199e7e52fa16ee6830b45ad4cdc62e51f62eb60c"},
"credo": {:hex, :credo, "1.7.6", "b8f14011a5443f2839b04def0b252300842ce7388f3af177157c86da18dfbeea", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "146f347fb9f8cbc5f7e39e3f22f70acbef51d441baa6d10169dd604bfbc55296"},
"credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"},
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
"dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"},
"doctor": {:hex, :doctor, "0.21.0", "20ef89355c67778e206225fe74913e96141c4d001cb04efdeba1a2a9704f1ab5", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "a227831daa79784eb24cdeedfa403c46a4cb7d0eab0e31232ec654314447e4e0"},
"earmark": {:hex, :earmark, "1.4.46", "8c7287bd3137e99d26ae4643e5b7ef2129a260e3dcf41f251750cb4563c8fb81", [:mix], [], "hexpm", "798d86db3d79964e759ddc0c077d5eb254968ed426399fbf5a62de2b5ff8910a"},
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
"ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"},
"earmark": {:hex, :earmark, "1.4.47", "7e7596b84fe4ebeb8751e14cbaeaf4d7a0237708f2ce43630cfd9065551f94ca", [:mix], [], "hexpm", "3e96bebea2c2d95f3b346a7ff22285bc68a99fbabdad9b655aa9c6be06c698f8"},
"earmark_parser": {:hex, :earmark_parser, "1.4.40", "f3534689f6b58f48aa3a9ac850d4f05832654fe257bf0549c08cc290035f70d5", [:mix], [], "hexpm", "cdb34f35892a45325bad21735fadb88033bcb7c4c296a999bde769783f53e46a"},
"ecto": {:hex, :ecto, "3.12.2", "bae2094f038e9664ce5f089e5f3b6132a535d8b018bd280a485c2f33df5c0ce1", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "492e67c70f3a71c6afe80d946d3ced52ecc57c53c9829791bfff1830ff5a1f0c"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ets": {:hex, :ets, "0.9.0", "79c6a6c205436780486f72d84230c6cba2f8a9920456750ddd1e47389107d5fd", [:mix], [], "hexpm", "2861fdfb04bcaeff370f1a5904eec864f0a56dcfebe5921ea9aadf2a481c822b"},
"ex_check": {:hex, :ex_check, "0.16.0", "07615bef493c5b8d12d5119de3914274277299c6483989e52b0f6b8358a26b5f", [:mix], [], "hexpm", "4d809b72a18d405514dda4809257d8e665ae7cf37a7aee3be6b74a34dec310f5"},
"ex_doc": {:hex, :ex_doc, "0.32.2", "f60bbeb6ccbe75d005763e2a328e6f05e0624232f2393bc693611c2d3ae9fa0e", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "a4480305cdfe7fdfcbb77d1092c76161626d9a7aa4fb698aee745996e34602df"},
"ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"},
"faker": {:hex, :faker, "0.18.0", "943e479319a22ea4e8e39e8e076b81c02827d9302f3d32726c5bf82f430e6e14", [:mix], [], "hexpm", "bfbdd83958d78e2788e99ec9317c4816e651ad05e24cfd1196ce5db5b3e81797"},
"file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"},
"git_cli": {:hex, :git_cli, "0.3.0", "a5422f9b95c99483385b976f5d43f7e8233283a47cda13533d7c16131cb14df5", [:mix], [], "hexpm", "78cb952f4c86a41f4d3511f1d3ecb28edb268e3a7df278de2faa1bd4672eaf9b"},
"git_ops": {:hex, :git_ops, "2.6.1", "cc7799a68c26cf814d6d1a5121415b4f5bf813de200908f930b27a2f1fe9dad5", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "ce62d07e41fe993ec22c35d5edb11cf333a21ddaead6f5d9868fcb607d42039e"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"glob_ex": {:hex, :glob_ex, "0.1.8", "f7ef872877ca2ae7a792ab1f9ff73d9c16bf46ecb028603a8a3c5283016adc07", [:mix], [], "hexpm", "9e39d01729419a60a937c9260a43981440c43aa4cadd1fa6672fecd58241c464"},
"igniter": {:hex, :igniter, "0.3.24", "791a91650ffab9d66b9a3011c66491f767577ad55c363f820cc188554207ee6f", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:inflex, "~> 2.0", [hex: :inflex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:owl, "~> 0.9", [hex: :owl, repo: "hexpm", optional: false]}, {:rewrite, "~> 0.9", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}, {:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: false]}], "hexpm", "2e1d336534c6129bae0db043fae650303b96974c0488c290191d6d4c61ec9a9f"},
"inflex": {:hex, :inflex, "2.1.0", "a365cf0821a9dacb65067abd95008ca1b0bb7dcdd85ae59965deef2aa062924c", [:mix], [], "hexpm", "14c17d05db4ee9b6d319b0bff1bdf22aa389a25398d1952c7a0b5f3d93162dd8"},
"iterex": {:hex, :iterex, "0.1.2", "58f9b9b9a22a55cbfc7b5234a9c9c63eaac26d276b3db80936c0e1c60355a5a6", [:mix], [], "hexpm", "2e103b8bcc81757a9af121f6dc0df312c9a17220f302b1193ef720460d03029d"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"},
"makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"},
"makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},
"makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"},
"mix_audit": {:hex, :mix_audit, "2.1.3", "c70983d5cab5dca923f9a6efe559abfb4ec3f8e87762f02bab00fa4106d17eda", [:make, :mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.9", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "8c3987100b23099aea2f2df0af4d296701efd031affb08d0746b2be9e35988ec"},
"mix_audit": {:hex, :mix_audit, "2.1.4", "0a23d5b07350cdd69001c13882a4f5fb9f90fbd4cbf2ebc190a2ee0d187ea3e9", [:make, :mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.11", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "fd807653cc8c1cada2911129c7eb9e985e3cc76ebf26f4dd628bb25bbcaa7099"},
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
"reactor": {:hex, :reactor, "0.8.2", "b2be82b1c3402537d06a8f85bb1849f72cb6b4be140495cb8956de7aec2fdebd", [:mix], [{:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c35eb23b77cc77ba922af108722ac93257899e35cfdd18882f0e659ad2cac9f3"},
"recase": {:hex, :recase, "0.7.0", "3f2f719f0886c7a3b7fe469058ec539cb7bbe0023604ae3bce920e186305e5ae", [:mix], [], "hexpm", "36f5756a9f552f4a94b54a695870e32f4e72d5fad9c25e61bc4a3151c08a4e0c"},
"sourceror": {:hex, :sourceror, "1.1.0", "9c129fa1bd7290014acf6f73e292f43938c17e3fccd7b7df6f41122cab45dda9", [:mix], [], "hexpm", "b9c348688e2cfc20acfef0feaca88643044be5acd2e0b02cf4a8d6ac1edc4c4a"},
"spark": {:hex, :spark, "2.1.22", "a36400eede64c51af578de5fdb5a5aaa3e0811da44bcbe7545fce059bd2a990b", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "f764611d0b15ac132e72b2326539acc11fc4e63baa3e429f541bca292b5f7064"},
"owl": {:hex, :owl, "0.11.0", "2cd46185d330aa2400f1c8c3cddf8d2ff6320baeff23321d1810e58127082cae", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "73f5783f0e963cc04a061be717a0dbb3e49ae0c4bfd55fb4b78ece8d33a65efe"},
"reactor": {:hex, :reactor, "0.9.1", "082f8e9b1fd7586c0a016c2fb533835fec7eaef5ffb0263abb4473106c20b1ca", [:mix], [{:igniter, "~> 0.2", [hex: :igniter, repo: "hexpm", optional: false]}, {:iterex, "~> 0.1", [hex: :iterex, repo: "hexpm", optional: false]}, {:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7191ddf95fdd2b65770a57a2e38dd502a94909e51ac8daf497330e67fc032dc3"},
"recase": {:hex, :recase, "0.8.1", "ab98cd35857a86fa5ca99036f575241d71d77d9c2ab0c39aacf1c9b61f6f7d1d", [:mix], [], "hexpm", "9fd8d63e7e43bd9ea385b12364e305778b2bbd92537e95c4b2e26fc507d5e4c2"},
"rewrite": {:hex, :rewrite, "0.10.5", "6afadeae0b9d843b27ac6225e88e165884875e0aed333ef4ad3bf36f9c101bed", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "51cc347a4269ad3a1e7a2c4122dbac9198302b082f5615964358b4635ebf3d4f"},
"sourceror": {:hex, :sourceror, "1.6.0", "9907884e1449a4bd7dbaabe95088ed4d9a09c3c791fb0103964e6316bc9448a7", [:mix], [], "hexpm", "e90aef8c82dacf32c89c8ef83d1416fc343cd3e5556773eeffd2c1e3f991f699"},
"spark": {:hex, :spark, "2.2.23", "78f0a1b0b713a91ad556fe9dc19ec92d977aaa0803cce2e255d90e58b9045c2a", [:mix], [{:igniter, ">= 0.2.6 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.2", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "a354b5cd7c3f021e3cd1da5a033b7643fe7b3c71c96b96d9f500a742f40c94db"},
"spitfire": {:hex, :spitfire, "0.1.3", "7ea0f544005dfbe48e615ed90250c9a271bfe126914012023fd5e4b6b82b7ec7", [:mix], [], "hexpm", "d53b5107bcff526a05c5bb54c95e77b36834550affd5830c9f58760e8c543657"},
"splode": {:hex, :splode, "0.2.4", "71046334c39605095ca4bed5d008372e56454060997da14f9868534c17b84b53", [:mix], [], "hexpm", "ca3b95f0d8d4b482b5357954fec857abd0fa3ea509d623334c1328e7382044c2"},
"stream_data": {:hex, :stream_data, "1.0.0", "c1380747a4650902732696861d5cb66ad3cb1cc93f31c2c8498bf87cddbabe2d", [:mix], [], "hexpm", "acd53e27c66c617d466f42ec77a7f59e5751f6051583c621ccdb055b9690435d"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"typable": {:hex, :typable, "0.3.0", "0431e121d124cd26f312123e313d2689b9a5322b15add65d424c07779eaa3ca1", [:mix], [], "hexpm", "880a0797752da1a4c508ac48f94711e04c86156f498065a83d160eef945858f8"},
"stream_data": {:hex, :stream_data, "1.1.1", "fd515ca95619cca83ba08b20f5e814aaf1e5ebff114659dc9731f966c9226246", [:mix], [], "hexpm", "45d0cd46bd06738463fd53f22b70042dbb58c384bb99ef4e7576e7bb7d3b8c8c"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
"ucwidth": {:hex, :ucwidth, "0.2.0", "1f0a440f541d895dff142275b96355f7e91e15bca525d4a0cc788ea51f0e3441", [:mix], [], "hexpm", "c1efd1798b8eeb11fb2bec3cafa3dd9c0c3647bee020543f0340b996177355bf"},
"yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"},
"yaml_elixir": {:hex, :yaml_elixir, "2.9.0", "9a256da867b37b8d2c1ffd5d9de373a4fda77a32a45b452f1708508ba7bbcb53", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "0cb0e7d4c56f5e99a6253ed1a670ed0e39c13fc45a6da054033928607ac08dfc"},
"yaml_elixir": {:hex, :yaml_elixir, "2.11.0", "9e9ccd134e861c66b84825a3542a1c22ba33f338d82c07282f4f1f52d847bd50", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "53cc28357ee7eb952344995787f4bb8cc3cecbf189652236e9b163e8ce1bc242"},
}

View file

@ -7,14 +7,13 @@ defmodule Smokestack.FactoryBuilderTest do
test "it can build attributes from a factory" do
{:ok, factory} = Info.factory(Factory, Post, :default)
assert {:ok, attrs} = Builder.build(FactoryBuilder, factory, [])
assert {:ok, attrs} = FactoryBuilder.build(factory, [])
assert byte_size(attrs[:title]) > 0
end
test "it allows attributes to be overridden" do
{:ok, factory} = Info.factory(Factory, Post, :default)
assert {:ok, %{title: "wat"}} =
Builder.build(FactoryBuilder, factory, attrs: %{title: "wat"})
assert {:ok, %{title: "wat"}} = FactoryBuilder.build(factory, attrs: %{title: "wat"})
end
end

View file

@ -7,14 +7,14 @@ defmodule Smokestack.ManyBuilderTest do
test "it can build a factory more than once" do
{:ok, factory} = Info.factory(Factory, Post, :default)
assert {:ok, results} = Builder.build(ManyBuilder, factory, count: 2)
assert {:ok, results} = ManyBuilder.build(factory, count: 2)
assert length(results) == 2
assert Enum.all?(results, &(byte_size(&1.title) > 0))
end
test "it errors when asked to build less than one instance" do
{:ok, factory} = Info.factory(Factory, Post, :default)
assert {:error, reason} = Builder.build(ManyBuilder, factory, count: 0)
assert Exception.message(reason) =~ ~r/expected positive integer/i
assert {:error, reason} = ManyBuilder.build(factory, count: 0)
assert Exception.message(reason) =~ ~r/positive integer/i
end
end

View file

@ -7,35 +7,35 @@ defmodule Smokestack.RecordBuilderTest do
test "it can build a single record" do
{:ok, factory} = Info.factory(Factory, Post, :default)
assert {:ok, record} = Builder.build(RecordBuilder, factory, [])
assert {:ok, record} = RecordBuilder.build(factory, [])
assert is_struct(record, Post)
assert record.__meta__.state == :loaded
end
test "it can build multiple records" do
{:ok, factory} = Info.factory(Factory, Post, :default)
assert {:ok, records} = Builder.build(RecordBuilder, factory, count: 2)
assert {:ok, records} = RecordBuilder.build(factory, count: 2)
assert length(records) == 2
assert Enum.all?(records, &(is_struct(&1, Post) && &1.__meta__.state == :loaded))
end
test "it can build directly related records" do
{:ok, factory} = Info.factory(Factory, Post, :default)
assert {:ok, record} = Builder.build(RecordBuilder, factory, build: :author)
assert {:ok, record} = RecordBuilder.build(factory, build: :author)
assert is_struct(record.author, Author)
assert record.author.__meta__.state == :loaded
end
test "it can build indirectly related records" do
{:ok, factory} = Info.factory(Factory, Post, :default)
assert {:ok, record} = Builder.build(RecordBuilder, factory, build: [author: :posts])
assert {:ok, record} = RecordBuilder.build(factory, build: [author: :posts])
assert [%Post{} = post] = record.author.posts
assert post.__meta__.state == :loaded
end
test "it can load calculations" do
{:ok, factory} = Info.factory(Factory, Post, :default)
assert {:ok, record} = Builder.build(RecordBuilder, factory, load: :full_title)
assert {:ok, record} = RecordBuilder.build(factory, load: :full_title)
assert record.full_title == record.title <> ": " <> record.sub_title
end
@ -43,7 +43,7 @@ defmodule Smokestack.RecordBuilderTest do
{:ok, factory} = Info.factory(Factory, Post, :default)
assert {:ok, record} =
Builder.build(RecordBuilder, factory,
RecordBuilder.build(factory,
load: [author: :count_of_posts],
build: [author: :posts]
)
@ -55,7 +55,7 @@ defmodule Smokestack.RecordBuilderTest do
{:ok, factory} = Info.factory(Factory, Post, :default)
assert {:ok, record} =
Builder.build(RecordBuilder, factory, build: :author, load: [author: :posts])
RecordBuilder.build(factory, build: :author, load: [author: :posts])
assert [post] = record.author.posts
assert post.id == record.id

View file

@ -7,21 +7,21 @@ defmodule Smokestack.RelatedBuilderTest do
test "it can build attributes from directly related factories" do
{:ok, factory} = Info.factory(Factory, Post, :default)
assert {:ok, attrs} = Builder.build(RelatedBuilder, factory, build: :author)
assert {:ok, attrs} = RelatedBuilder.build(factory, build: :author)
assert byte_size(attrs[:author][:name]) > 0
end
test "it can build attributes from indirectly related factories" do
{:ok, factory} = Info.factory(Factory, Post, :default)
assert {:ok, attrs} = Builder.build(RelatedBuilder, factory, build: [author: :posts])
assert {:ok, attrs} = RelatedBuilder.build(factory, build: [author: :posts])
assert [post] = attrs[:author][:posts]
assert byte_size(post[:title]) > 0
end
test "it can attach directly related records" do
{:ok, factory} = Info.factory(Factory, Post, :default)
{:ok, author} = Builder.build(RecordBuilder, Info.factory!(Factory, Author, :default), [])
{:ok, attrs} = Builder.build(RelatedBuilder, factory, relate: [author: author])
{:ok, author} = RecordBuilder.build(Info.factory!(Factory, Author, :default), [])
{:ok, attrs} = RelatedBuilder.build(factory, relate: [author: author])
assert attrs[:author] == author
end
end

View file

@ -1,6 +1,7 @@
defmodule Smokestack.DslTest do
use ExUnit.Case, async: true
alias Spark.Error.DslError
alias Support.Author
defmodule Post do
@moduledoc false
@ -18,6 +19,18 @@ defmodule Smokestack.DslTest do
attribute :title, :string
end
relationships do
belongs_to :author, Author
end
calculations do
calculate :title_first_word, :string, expr(title |> string_split() |> at(0))
end
actions do
defaults [:read]
end
end
defmodule Factory do
@ -107,4 +120,101 @@ defmodule Smokestack.DslTest do
assert %Post{title: title} = FactoryUser.test()
assert title =~ ~r/[a-z]+/i
end
test "before build hooks can be applied" do
defmodule BeforeBuildFactory do
@moduledoc false
use Smokestack
factory Post do
attribute :title, &Faker.Company.catch_phrase/0
before_build &capitalise_title/1
end
def capitalise_title(record) do
%{record | title: String.upcase(record.title)}
end
end
title = Faker.Company.catch_phrase()
upper_title = String.upcase(title)
assert %Post{title: ^upper_title} = BeforeBuildFactory.insert!(Post, attrs: %{title: title})
end
test "after build hooks can be applied" do
defmodule AfterBuildFactory do
@moduledoc false
use Smokestack
factory Post do
attribute :title, &Faker.Company.catch_phrase/0
after_build &add_metadata/1
end
def add_metadata(record) do
Ash.Resource.put_metadata(record, :wat, true)
end
end
assert %Post{__metadata__: %{wat: true}} = AfterBuildFactory.insert!(Post)
end
test "auto builds can be specified in the factory" do
defmodule AutoBuildFactory do
@moduledoc false
use Smokestack
factory Post do
attribute :title, &Faker.Company.catch_phrase/0
auto_build :author
end
factory Author do
attribute :name, &Faker.Internet.email/0
attribute :email, &Faker.Person.name/0
end
end
assert %Post{author: %Author{}} = AutoBuildFactory.insert!(Post)
end
test "auto builds can be overridden at runtime" do
defmodule AutoBuildRelateFactory do
@moduledoc false
use Smokestack
factory Post do
attribute :title, &Faker.Company.catch_phrase/0
auto_build :author
end
factory Author do
attribute :name, &Faker.Internet.email/0
attribute :email, &Faker.Person.name/0
end
end
author = AutoBuildRelateFactory.insert!(Author)
post = AutoBuildRelateFactory.insert!(Post, relate: [author: author])
# The auto-build should not be used - the existing author should be used
assert Ash.count!(Author, domain: Support.Domain) == 1
assert post.author.id == author.id
end
test "auto loads can be specified in the factory" do
defmodule AutoLoadFactory do
@moduledoc false
use Smokestack
factory Post do
attribute :title, &Faker.Company.catch_phrase/0
auto_load :title_first_word
domain Support.Domain
end
end
assert post = AutoLoadFactory.insert!(Post)
assert post.title_first_word == post.title |> String.split(" ") |> List.first()
end
end

View file

@ -67,4 +67,17 @@ defmodule Smokestack.OptionTest do
assert post.author.id == author.id
end
end
describe "variant" do
test "it can select a variant at build time" do
assert {:ok, author} = insert(Author, variant: :trek)
assert to_string(author.email) =~ ~r/\.(starfleet|rebellion)$/
end
end
test "it validates all options" do
assert {:error, error} = insert(Author, sss: 2)
message = Exception.message(error)
assert message =~ ~r/required :[a-z]+ option not found/
end
end

View file

@ -13,9 +13,9 @@ defmodule Support.Factory do
attribute :email, fn
%{name: "JL"} -> "captain@entrepreneur.starfleet"
%{name: "Doc Holoday"} -> "cheifmed@voyager.starfleet"
%{name: "Dr Mark"} -> "cheifmed@voyager.starfleet"
%{name: "BLT"} -> "cheifeng@voyager.starfleet"
%{name: "Cal Hudson"} -> "cal@maquis.stfu"
%{name: "Cal Hudson"} -> "cal@maquis.rebellion"
end
end