Compare commits

...

8 commits

Author SHA1 Message Date
James Harton 85113e4ebd
chore(CI): Simplify CI configuration.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-08-19 18:03:58 +12:00
James Harton 9f3248dbc9 chore: release version v0.1.1
Some checks failed
continuous-integration/drone/push Build is failing
2023-08-18 08:53:50 +00:00
James Harton 15117cf1bb
improvement: Add insert_many.
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-18 20:46:27 +12:00
James Harton f2df421786
improvement: Add Smokestack behaviour. 2023-08-18 20:14:53 +12:00
James Harton 408c813320
improvement: Add Builder.insert/2..5. 2023-08-17 20:19:40 +12:00
James Harton ac2521c3c3
chore: add missing git_ops config.
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-17 19:43:48 +12:00
Renovate Bot f6f473f753 chore(deps): update dependency elixir to v1.15.4
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2023-08-17 06:10:51 +12:00
Renovate Bot 361bfad584 chore(deps): add renovate.json
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2023-08-16 22:13:01 +12:00
12 changed files with 379 additions and 220 deletions

View file

@ -26,7 +26,6 @@ steps:
- name: restore build cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
@ -73,7 +72,6 @@ steps:
- name: store ASDF cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
@ -97,7 +95,6 @@ steps:
- name: store build cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
@ -123,65 +120,8 @@ steps:
- .mix
- .rebar3
---
kind: pipeline
type: docker
name: test
depends_on:
- build
steps:
- name: restore ASDF cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: SECRET_ACCESS_KEY
AWS_PLUGIN_PATH_STYLE: true
settings:
restore: true
endpoint:
from_secret: S3_ENDPOINT
bucket:
from_secret: CACHE_BUCKET
region: us-east-1
path-style: true
cache_key: 'asdf-{{ os }}-{{ arch }}-{{ checksum ".tool-versions" }}'
mount:
- .asdf
- name: restore build cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: SECRET_ACCESS_KEY
AWS_PLUGIN_PATH_STYLE: true
settings:
restore: true
endpoint:
from_secret: S3_ENDPOINT
bucket:
from_secret: CACHE_BUCKET
region: us-east-1
path-style: true
cache_key: 'elixir-{{ checksum "mix.lock" }}-{{ checksum ".tool-versions" }}'
mount:
- deps
- _build
- .hex
- .mix
- .rebar3
- name: mix compile
image: code.harton.nz/james/asdf_container:latest
pull: "always"
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
@ -189,14 +129,12 @@ steps:
REBAR_BASE_DIR: /drone/src/.rebar3
ASDF_DATA_DIR: /drone/src/.asdf
depends_on:
- restore ASDF cache
- restore build cache
- install dependencies
commands:
- asdf mix compile --warnings-as-errors
- name: mix test
image: code.harton.nz/james/asdf_container:latest
pull: "always"
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
@ -210,7 +148,6 @@ steps:
- name: mix credo
image: code.harton.nz/james/asdf_container:latest
pull: "always"
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
@ -224,7 +161,6 @@ steps:
- name: mix hex.audit
image: code.harton.nz/james/asdf_container:latest
pull: "always"
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
@ -238,7 +174,6 @@ steps:
- name: mix format
image: code.harton.nz/james/asdf_container:latest
pull: "always"
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
@ -252,7 +187,6 @@ steps:
- name: mix spark.formatter
image: code.harton.nz/james/asdf_container:latest
pull: "always"
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
@ -266,7 +200,6 @@ steps:
- name: mix deps.unlock
image: code.harton.nz/james/asdf_container:latest
pull: "always"
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
@ -280,7 +213,6 @@ steps:
- name: mix doctor
image: code.harton.nz/james/asdf_container:latest
pull: "always"
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
@ -294,7 +226,6 @@ steps:
- name: mix git_ops.check_message
image: code.harton.nz/james/asdf_container:latest
pull: "always"
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
@ -307,73 +238,20 @@ steps:
- git log -1 --format=%s > .last_commit_message
- asdf mix git_ops.check_message .last_commit_message
---
kind: pipeline
type: docker
name: git ops
trigger:
branch:
- main
event:
- push
depends_on:
- test
steps:
- name: restore ASDF cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: SECRET_ACCESS_KEY
AWS_PLUGIN_PATH_STYLE: true
settings:
restore: true
endpoint:
from_secret: S3_ENDPOINT
bucket:
from_secret: CACHE_BUCKET
region: us-east-1
path-style: true
cache_key: 'asdf-{{ os }}-{{ arch }}-{{ checksum ".tool-versions" }}'
mount:
- .asdf
- name: restore build cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: SECRET_ACCESS_KEY
AWS_PLUGIN_PATH_STYLE: true
settings:
restore: true
endpoint:
from_secret: S3_ENDPOINT
bucket:
from_secret: CACHE_BUCKET
region: us-east-1
path-style: true
cache_key: 'elixir-{{ checksum "mix.lock" }}-{{ checksum ".tool-versions" }}'
mount:
- deps
- _build
- .hex
- .mix
- .rebar3
- name: mix git_ops.release
image: code.harton.nz/james/asdf_container:latest
pull: "always"
when:
branch:
- main
depends_on:
- restore ASDF cache
- restore build cache
- mix test
- mix credo
- mix hex.audit
- mix format
- mix spark.formatter
- mix deps.unlock
- mix doctor
- mix git_ops.check_message
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
@ -398,73 +276,23 @@ steps:
- git push $${GIT_URL} "HEAD:${DRONE_COMMIT_REF}" "refs/tags/v$${NEW_APP_VERSION}"
- fi
---
kind: pipeline
type: docker
name: release
trigger:
ref:
include:
- refs/tags/v**
depends_on:
- test
steps:
- name: restore ASDF cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: SECRET_ACCESS_KEY
AWS_PLUGIN_PATH_STYLE: true
settings:
restore: true
endpoint:
from_secret: S3_ENDPOINT
bucket:
from_secret: CACHE_BUCKET
region: us-east-1
path-style: true
cache_key: 'asdf-{{ os }}-{{ arch }}-{{ checksum ".tool-versions" }}'
mount:
- .asdf
- name: restore build cache
image: meltwater/drone-cache
pull: "always"
environment:
AWS_ACCESS_KEY_ID:
from_secret: ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
from_secret: SECRET_ACCESS_KEY
AWS_PLUGIN_PATH_STYLE: true
settings:
restore: true
endpoint:
from_secret: S3_ENDPOINT
bucket:
from_secret: CACHE_BUCKET
region: us-east-1
path-style: true
cache_key: 'elixir-{{ checksum "mix.lock" }}-{{ checksum ".tool-versions" }}'
mount:
- deps
- _build
- .hex
- .mix
- .rebar3
- name: build artifacts
image: code.harton.nz/james/asdf_container:latest
pull: "always"
when:
event:
- tag
refs:
include:
- refs/tags/v*
depends_on:
- restore ASDF cache
- restore build cache
- mix test
- mix credo
- mix hex.audit
- mix format
- mix spark.formatter
- mix deps.unlock
- mix doctor
- mix git_ops.check_message
environment:
MIX_ENV: test
HEX_HOME: /drone/src/.hex
@ -477,7 +305,6 @@ steps:
- mix git_ops.project_info --format=shell > app.env
- . ./app.env
- mkdir artifacts
- mix esbuild prod
- mix hex.build -o "artifacts/$${APP_NAME}-$${APP_VERSION}-pkg.tar"
- gzip "artifacts/$${APP_NAME}-$${APP_VERSION}-pkg.tar"
- mix docs
@ -487,6 +314,12 @@ steps:
- name: gitea release
image: plugins/gitea-release
when:
event:
- tag
refs:
include:
- refs/tags/v*
depends_on:
- build artifacts
settings:
@ -499,6 +332,12 @@ steps:
note: tag_body
- name: docs release
when:
event:
- tag
refs:
include:
- refs/tags/v*
image: minio/mc
environment:
S3_ENDPOINT:
@ -519,9 +358,14 @@ steps:
# - name: hex release
# image: code.harton.nz/james/asdf_container:latest
# pull: "always"
# when:
# event:
# - tag
# refs:
# include:
# - refs/tags/v*
# depends_on:
# - restore ASDF cache
# - restore build cache
# - build artifacts
# environment:
# MIX_ENV: test
# HEX_HOME: /drone/src/.hex

View file

@ -1,2 +1,2 @@
elixir 1.15.4-otp-26
elixir 1.15.4
erlang 26.0.2

View file

@ -5,6 +5,19 @@ See [Conventional Commits](Https://conventionalcommits.org) for commit guideline
<!-- changelog -->
## [v0.1.1](https://code.harton.nz/james/smokestack/compare/v0.1.0...v0.1.1) (2023-08-18)
### Improvements:
* Add `insert_many`.
* Add `Smokestack` behaviour.
* Add `Builder.insert/2..5`.
## [v0.1.0](https://code.harton.nz/james/smokestack/compare/v0.1.0...v0.1.0) (2023-08-16)

9
config/config.exs Normal file
View file

@ -0,0 +1,9 @@
import Config
config :git_ops,
mix_project: Mix.Project.get!(),
changelog_file: "CHANGELOG.md",
repository_url: "https://code.harton.nz/james/smokestack",
manage_mix_version?: true,
version_tag_prefix: "v",
manage_readme_version: "README.md"

View file

@ -19,6 +19,144 @@ defmodule Smokestack do
"""
use Dsl, default_extensions: [extensions: [Smokestack.Dsl]]
alias Ash.Resource
alias Smokestack.Builder
@type t :: module
@doc """
Runs a factory and uses it to build a map or list of results.
Automatically implemented by modules which `use Smokestack`.
See `Smokestack.Builder.params/5` for more information.
"""
@callback params(Resource.t(), map, atom, Builder.param_options()) ::
{:ok, Builder.param_result()} | {:error, any}
@doc """
Raising version of `params/4`.
Automatically implemented by modules which `use Smokestack`.
See `Smokestack.Builder.params/5` for more information.
"""
@callback params!(Resource.t(), map, atom, Builder.param_options()) ::
Builder.param_result() | no_return
@doc """
Runs a factory and uses it to insert an Ash Resource into it's data layer.
Automatically implemented by modules which `use Smokestack`.
See `Smokestack.Builder.insert/5` for more information.
"""
@callback insert(Resource.t(), map, atom, Builder.insert_options()) ::
{:ok, Resource.record()} | {:error, any}
@doc """
Raising version of `insert/4`.
Automatically implemented by modules which `use Smokestack`.
See `Smokestack.Builder.insert/5` for more information.
"""
@callback insert!(Resource.t(), map, atom, Builder.insert_options()) ::
Resource.record() | no_return
@doc """
Runs a factory a number of times and returns a list of created records.
Automatically implemented by modules which `use Smokestack`.
See `Smokestack.Builder.insert_many/5` for more information.
"""
@callback insert_many(Resource.t(), pos_integer, atom, Builder.insert_options()) ::
{:ok, [Resource.record()]} | {:error, any}
@doc """
Raising version of `insert_many/4`.
Automatically implemented by modules which `use Smokestack`.
See `Smokestack.Builder.insert_many/5` for more information.
"""
@callback insert_many!(Resource.t(), pos_integer, atom, Builder.insert_options()) ::
[Resource.record()] | no_return
@doc false
defmacro __using__(opts) do
[
quote do
@behaviour Smokestack
@doc """
Execute the matching factory and return a map or list of params.
See `Smokestack.Builder.params/5` for more information.
"""
@spec params(Resource.t(), map, atom, Builder.param_options()) ::
{:ok, Builder.param_result()} | {:error, any}
def params(resource, overrides \\ %{}, variant \\ :default, options \\ []),
do: Builder.params(__MODULE__, resource, overrides, variant, options)
@doc """
Raising version of `params/4`.
See `Smokestack.Builder.params/5` for more information.
"""
@spec params!(Resource.t(), map, atom, Builder.param_options()) ::
Builder.param_result() | no_return
def params!(resource, overrides \\ %{}, variant \\ :default, options \\ []),
do: Builder.params!(__MODULE__, resource, overrides, variant, options)
@doc """
Execute the matching factory and return an inserted Ash Resource record.
See `Smokestack.Builder.insert/5` for more information.
"""
@spec insert(Resource.t(), map, atom, Builder.insert_options()) ::
{:ok, Resource.record()} | {:error, any}
def insert(resource, overrides \\ %{}, variant \\ :default, options \\ []),
do: Builder.insert(__MODULE__, resource, overrides, variant, options)
@doc """
Raising version of `insert/4`.
See `Smokestack.Builder.insert/5` for more information.
"""
@spec insert!(Resource.t(), map, atom, Builder.insert_options()) ::
Resource.record() | no_return
def insert!(resource, overrides \\ %{}, variant \\ :default, options \\ []),
do: Builder.insert!(__MODULE__, resource, overrides, variant, options)
@doc """
Execute the matching factory a number of times and return a list of Ash Resource records.
See `Smokestack.Builder.insert_many/5` for more information.
"""
@spec insert_many(Resource.t(), pos_integer, atom, Builder.insert_options()) ::
{:ok, [Resource.record()]} | {:error, any}
def insert_many(resource, count, variant \\ :default, options \\ []),
do: Builder.insert_many(__MODULE__, resource, count, variant, options)
@doc """
Raising version of `insert_many/4`.
See `Smokestack.Builder.insert_many/5` for more information.
"""
@spec insert_many!(Resource.t(), pos_integer, atom, Builder.insert_options()) ::
[Resource.record()] | no_return
def insert_many!(resource, count, variant \\ :default, options \\ []),
do: Builder.insert_many!(__MODULE__, resource, count, variant, options)
defoverridable params: 4,
params!: 4,
insert: 4,
insert!: 4,
insert_many: 4,
insert_many!: 4
end
] ++ super(opts)
end
end

View file

@ -3,13 +3,13 @@ defmodule Smokestack.Builder do
Handles the building of parameters and records.
"""
alias Ash.Resource
alias Ash.{Resource, Seed}
alias Smokestack.{Dsl.Attribute, Dsl.Info, Template}
@param_option_defaults [keys: :atom, as: :map]
@typedoc "Options that can be passed to `params/4`."
@type param_options :: [param_keys_option | param_as_option]
@type param_options :: [param_keys_option | param_as_option | build_option]
@typedoc "Key type in the result. Defaults to `#{inspect(@param_option_defaults[:keys])}`."
@type param_keys_option :: {:keys, :atom | :string | :dasherise}
@ -18,17 +18,22 @@ defmodule Smokestack.Builder do
@type param_as_option :: {:as, :map | :list}
@type param_result ::
%{required(String.t()) => any}
| %{required(atom) => any}
| [{String.t(), any}]
| [{atom, any}]
%{required(atom | String.t()) => any}
| [{atom | String.t(), any}]
@type insert_options :: [build_option]
@typedoc "A nested keyword list of associations that should also be built"
@type build_option :: {:build, Keyword.t(atom | Keyword.t())}
@type insert_result :: Resource.record()
@doc """
Build parameters for a resource with a factory.
"""
@spec params(Smokestack.t(), Resource.t(), atom, param_options) ::
@spec params(Smokestack.t(), Resource.t(), map, atom, param_options) ::
{:ok, param_result} | {:error, any}
def params(factory_module, resource, variant \\ :default, overrides \\ %{}, options \\ [])
def params(factory_module, resource, overrides \\ %{}, variant \\ :default, options \\ [])
when is_atom(factory_module) and is_atom(resource) and is_atom(variant) and is_list(options) do
with {:ok, factory} <- get_factory(factory_module, resource, variant),
{:ok, params} <- build_params(factory, overrides, options) do
@ -43,14 +48,107 @@ defmodule Smokestack.Builder do
end
@doc "Raising version of `params/2..5`."
@spec params!(Smokestack.t(), Resource.t(), atom, param_options) :: param_result | no_return
def params!(factory_module, resource, variant \\ :default, overrides \\ %{}, options \\ []) do
case params(factory_module, resource, variant, overrides, options) do
@spec params!(Smokestack.t(), Resource.t(), map, atom, param_options) ::
param_result | no_return
def params!(factory_module, resource, overrides \\ %{}, variant \\ :default, options \\ []) do
case params(factory_module, resource, overrides, variant, options) do
{:ok, params} -> params
{:error, reason} -> raise reason
end
end
@doc """
Build a resource and insert it into it's datalayer.
"""
@spec insert(Smokestack.t(), Resource.t(), map, atom, insert_options) ::
{:ok, insert_result} | {:error, any}
def insert(factory_module, resource, overrides \\ %{}, variant \\ :default, options \\ [])
when is_atom(factory_module) and is_atom(resource) and is_atom(Variant) and is_list(options) do
with {:ok, factory} <- get_factory(factory_module, resource, variant),
{:ok, params} <- build_params(factory, overrides, options) do
record =
resource
|> Seed.seed!(params)
|> Resource.put_metadata(:factory, factory_module)
|> Resource.put_metadata(:variant, variant)
{:ok, record}
end
rescue
error -> {:error, error}
end
@doc "Raising version of `insert/2..5`"
@spec insert!(Smokestack.t(), Resource.t(), map, atom, insert_options) ::
insert_result | no_return
def insert!(factory_module, resource, overrides \\ %{}, variant \\ :default, options \\ [])
when is_atom(factory_module) and is_atom(resource) and is_atom(variant) and
is_map(overrides) and is_list(options) do
with {:ok, factory} <- get_factory(factory_module, resource, variant),
{:ok, params} <- build_params(factory, overrides, options) do
resource
|> Seed.seed!(params)
|> Resource.put_metadata(:factory, factory_module)
|> Resource.put_metadata(:variant, variant)
else
{:error, reason} -> raise reason
end
end
@doc """
Build a number of resources and insert them into their datalayer.
"""
@spec insert_many(Smokestack.t(), Resource.t(), pos_integer, atom, insert_options) ::
{:ok, [insert_result]} | {:error, any}
def insert_many(factory_module, resource, count, variant \\ :default, options \\ [])
when is_atom(factory_module) and is_atom(resource) and is_integer(count) and count > 0 and
is_atom(variant) and is_list(options) do
with {:ok, factory} <- get_factory(factory_module, resource, variant),
{:ok, params_list} <- build_many_params(factory, count, options) do
records =
resource
|> Seed.seed!(params_list)
|> Enum.map(fn record ->
record
|> Resource.put_metadata(:factory, factory_module)
|> Resource.put_metadata(:variant, variant)
end)
{:ok, records}
end
rescue
error -> {:error, error}
end
@doc "Raising version of `insert_many/5`."
@spec insert_many!(Smokestack.t(), Resource.t(), pos_integer, atom, insert_options) ::
[insert_result] | no_return
def insert_many!(factory_module, resource, count, variant \\ :default, options \\ [])
when is_atom(factory_module) and is_atom(resource) and is_integer(count) and count > 0 and
is_atom(variant) and is_list(options) do
with {:ok, factory} <- get_factory(factory_module, resource, variant),
{:ok, params_list} <- build_many_params(factory, count, options) do
resource
|> Seed.seed!(params_list)
|> Enum.map(fn record ->
record
|> Resource.put_metadata(:factory, factory_module)
|> Resource.put_metadata(:variant, variant)
end)
else
{:error, reason} -> raise reason
end
end
defp build_many_params(factory, count, options) do
Enum.reduce_while(1..count, {:ok, []}, fn _, {:ok, params_list} ->
case build_params(factory, %{}, options) do
{:ok, params} -> {:cont, {:ok, [params | params_list]}}
{:error, reason} -> {:halt, {:error, reason}}
end
end)
end
defp get_factory(factory_module, resource, variant) do
with :error <- Info.factory(factory_module, resource, variant) do
{:error,
@ -70,15 +168,24 @@ defmodule Smokestack.Builder do
{:ok, Map.put(attrs, attr.name, override)}
:error ->
value = Template.generate(attr.generator, attrs, options)
generator = maybe_initialise_generator(attr)
value = Template.generate(generator, attrs, options)
{:ok, Map.put(attrs, attr.name, value)}
end
end)
end
defp maybe_initialise_generator(attr) do
with nil <- Process.get(attr.__identifier__),
generator <- Template.init(attr.generator) do
Process.put(attr.__identifier__, generator)
generator
end
end
defp maybe_stringify_keys(attrs, options) do
if Keyword.get(options, :keys, @param_option_defaults[:keys]) == :string do
Map.new(fn {key, value} -> {Atom.to_string(key), value} end)
Map.new(attrs, fn {key, value} -> {Atom.to_string(key), value} end)
else
attrs
end
@ -86,7 +193,7 @@ defmodule Smokestack.Builder do
defp maybe_dasherise_keys(attrs, options) do
if Keyword.get(options, :keys, @param_option_defaults[:keys]) == :dasherise do
Map.new(fn {key, value} ->
Map.new(attrs, fn {key, value} ->
key =
key
|> Atom.to_string()

View file

@ -5,12 +5,13 @@ defmodule Smokestack.Dsl.Attribute do
See `d:Smokestack.factory.default.attribute` for more information.
"""
defstruct generator: nil, name: nil
defstruct __identifier__: nil, generator: nil, name: nil
alias Ash.Resource
alias Spark.Dsl.Entity
@type t :: %__MODULE__{
__identifier__: nil,
generator:
mfa | (-> any) | (Resource.record() -> any) | (Resource.record(), keyword -> any),
name: atom
@ -24,6 +25,7 @@ defmodule Smokestack.Dsl.Attribute do
name: :attribute,
target: __MODULE__,
args: [:name, :generator],
identifier: {:auto, :unique_integer},
schema: [
name: [
type: :atom,

View file

@ -5,13 +5,14 @@ defmodule Smokestack.Dsl.Factory do
See `d:Smokestack.factory` for more information.
"""
defstruct attributes: [], resource: nil, variant: :default
defstruct __identifier__: nil, attributes: [], resource: nil, variant: :default
alias Ash.Resource
alias Smokestack.Dsl.{Attribute, Template}
alias Spark.Dsl.Entity
@type t :: %__MODULE__{
__identifier__: any,
attributes: [Attribute.t()],
resource: Resource.t(),
variant: atom
@ -27,6 +28,7 @@ defmodule Smokestack.Dsl.Factory do
target: __MODULE__,
args: [:resource, {:optional, :variant, :default}],
imports: [Template],
identifier: {:auto, :unique_integer},
schema: [
resource: [
type: {:behaviour, Ash.Resource},

View file

@ -1,7 +1,7 @@
defmodule Smokestack.MixProject do
use Mix.Project
@version "0.1.0"
@version "0.1.1"
@moduledoc """
Test factories for Ash resources.

6
renovate.json Normal file
View file

@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"local>renovate/renovate"
]
}

View file

@ -8,10 +8,47 @@ defmodule Smokestack.BuilderTest do
describe "params/2..5" do
test "it builds params" do
assert {:ok, params} = Builder.params(Factory, Post)
assert params |> Map.keys() |> Enum.sort() == ~w[body tags title]a
assert params |> Map.keys() |> Enum.sort() == ~w[body sub_title tags title]a
assert is_binary(params.body)
assert Enum.all?(params.tags, &is_binary/1)
assert is_binary(params.title)
end
test "it honours the `as: :list` option" do
assert {:ok, params} = Builder.params(Factory, Post, %{}, :default, as: :list)
assert is_list(params)
assert is_binary(params[:body])
assert Enum.all?(params[:tags], &is_binary/1)
assert is_binary(params[:title])
end
test "it honours the `keys: :string` option" do
assert {:ok, params} = Builder.params(Factory, Post, %{}, :default, keys: :string)
assert is_binary(params["body"])
assert Enum.all?(params["tags"], &is_binary/1)
assert is_binary(params["title"])
end
test "it honours the `keys: :dasherise` option" do
assert {:ok, params} = Builder.params(Factory, Post, %{}, :default, keys: :dasherise)
assert is_binary(params["sub-title"])
end
end
describe "insert/2..5" do
test "it inserts the resource" do
assert {:ok, record} = Builder.insert(Factory, Post)
assert is_struct(record, Post)
assert record.inserted_at
assert is_binary(record.title)
assert is_binary(record.sub_title)
assert Enum.all?(record.tags, &is_struct(&1, Ash.CiString))
end
end
describe "insert_many/3..5" do
assert {:ok, records} = Builder.insert_many(Factory, Post, 3)
assert length(records) == 3
assert Enum.all?(records, &is_struct(&1, Post))
end
end

View file

@ -23,6 +23,7 @@ defmodule Support.Factory do
attribute :title, &Faker.Commerce.product_name/0
attribute :tags, n_times(3..20, &Faker.Lorem.word/0)
attribute :body, &Faker.Markdown.markdown/0
attribute :sub_title, &Faker.Lorem.sentence/0
end
factory Support.Post, :trek do