zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

commit 761bdaabe3cbd6495289bf10054b892647a5c65a
parent 2ae2f0164ab1eaf6194f4c52d442df0c2c69230a
Author: srfsh <dev@srf.sh>
Date:   Tue, 23 Aug 2022 10:43:58 +0300

Zenflows{Test,}.VF.RecipeProcess: add paging support and small improvements

Diffstat:
Msrc/zenflows/vf/recipe_process/domain.ex | 51+++++++++++++++++++++++++++------------------------
Msrc/zenflows/vf/recipe_process/resolv.ex | 20++++++++++++--------
Msrc/zenflows/vf/recipe_process/type.ex | 34++++++++++++++++++++++++----------
Mtest/vf/recipe_process/domain.test.exs | 122++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mtest/vf/recipe_process/type.test.exs | 201+++++++++++++++++++++++++++++++++----------------------------------------------
5 files changed, 206 insertions(+), 222 deletions(-)

diff --git a/src/zenflows/vf/recipe_process/domain.ex b/src/zenflows/vf/recipe_process/domain.ex @@ -20,35 +20,48 @@ defmodule Zenflows.VF.RecipeProcess.Domain do alias Ecto.Multi alias Zenflows.DB.Repo +alias Zenflows.GQL.Paging alias Zenflows.VF.{Duration, RecipeProcess} @typep repo() :: Ecto.Repo.t() @typep chgset() :: Ecto.Changeset.t() -@typep changes() :: Ecto.Multi.changes() @typep id() :: Zenflows.DB.Schema.id() @typep params() :: Zenflows.DB.Schema.params() -@spec by_id(repo(), id()) :: RecipeProcess.t() | nil -def by_id(repo \\ Repo, id) do - repo.get(RecipeProcess, id) +@spec one(repo(), id() | map() | Keyword.t()) + :: {:ok, RecipeProcess.t()} | {:error, String.t()} +def one(repo \\ Repo, _) +def one(repo, id) when is_binary(id), do: one(repo, id: id) +def one(repo, clauses) do + case repo.get_by(RecipeProcess, clauses) do + nil -> {:error, "not found"} + found -> {:ok, found} + end +end + +@spec all(Paging.params()) :: Paging.result(RecipeProcess.t()) +def all(params) do + Paging.page(RecipeProcess, params) end @spec create(params()) :: {:ok, RecipeProcess.t()} | {:error, chgset()} def create(params) do Multi.new() - |> Multi.insert(:rec_proc, RecipeProcess.chgset(params)) + |> Multi.insert(:insert, RecipeProcess.chgset(params)) |> Repo.transaction() |> case do - {:ok, %{rec_proc: rp}} -> {:ok, rp} + {:ok, %{insert: rp}} -> {:ok, rp} {:error, _, cset, _} -> {:error, cset} end end -@spec update(id(), params()) :: {:ok, RecipeProcess.t()} | {:error, chgset()} +@spec update(id(), params()) + :: {:ok, RecipeProcess.t()} | {:error, String.t() | chgset()} def update(id, params) do Multi.new() - |> Multi.run(:get, multi_get(id)) - |> Multi.update(:update, &RecipeProcess.chgset(&1.get, params)) + |> Multi.put(:id, id) + |> Multi.run(:one, &one/2) + |> Multi.update(:update, &RecipeProcess.chgset(&1.one, params)) |> Repo.transaction() |> case do {:ok, %{update: rp}} -> {:ok, rp} @@ -56,11 +69,13 @@ def update(id, params) do end end -@spec delete(id()) :: {:ok, RecipeProcess.t()} | {:error, chgset()} +@spec delete(id()) + :: {:ok, RecipeProcess.t()} | {:error, String.t() | chgset()} def delete(id) do Multi.new() - |> Multi.run(:get, multi_get(id)) - |> Multi.delete(:delete, &(&1.get)) + |> Multi.put(:id, id) + |> Multi.run(:one, &one/2) + |> Multi.delete(:delete, & &1.one) |> Repo.transaction() |> case do {:ok, %{delete: rp}} -> {:ok, rp} @@ -77,16 +92,4 @@ end def preload(rec_proc, :process_conforms_to) do Repo.preload(rec_proc, :process_conforms_to) end - -# Returns a RecipeProcess in ok-err tuple from given ID. Used inside -# Ecto.Multi.run/5 to get a record in transaction. -@spec multi_get(id()) :: (repo(), changes() -> {:ok, RecipeProcess.t()} | {:error, String.t()}) -defp multi_get(id) do - fn repo, _ -> - case by_id(repo, id) do - nil -> {:error, "not found"} - rp -> {:ok, rp} - end - end -end end diff --git a/src/zenflows/vf/recipe_process/resolv.ex b/src/zenflows/vf/recipe_process/resolv.ex @@ -20,36 +20,40 @@ defmodule Zenflows.VF.RecipeProcess.Resolv do use Absinthe.Schema.Notation -alias Zenflows.VF.{RecipeProcess, RecipeProcess.Domain} +alias Zenflows.VF.RecipeProcess.Domain -def recipe_process(%{id: id}, _info) do - {:ok, Domain.by_id(id)} +def recipe_process(params, _) do + Domain.one(params) end -def create_recipe_process(%{recipe_process: params}, _info) do +def recipe_processes(params, _) do + Domain.all(params) +end + +def create_recipe_process(%{recipe_process: params}, _) do with {:ok, rec_proc} <- Domain.create(params) do {:ok, %{recipe_process: rec_proc}} end end -def update_recipe_process(%{recipe_process: %{id: id} = params}, _info) do +def update_recipe_process(%{recipe_process: %{id: id} = params}, _) do with {:ok, rec_proc} <- Domain.update(id, params) do {:ok, %{recipe_process: rec_proc}} end end -def delete_recipe_process(%{id: id}, _info) do +def delete_recipe_process(%{id: id}, _) do with {:ok, _} <- Domain.delete(id) do {:ok, true} end end -def has_duration(%RecipeProcess{} = rec_proc, _args, _info) do +def has_duration(rec_proc, _, _) do rec_proc = Domain.preload(rec_proc, :has_duration) {:ok, rec_proc.has_duration} end -def process_conforms_to(%RecipeProcess{} = rec_proc, _args, _info) do +def process_conforms_to(rec_proc, _, _) do rec_proc = Domain.preload(rec_proc, :process_conforms_to) {:ok, rec_proc.process_conforms_to} end diff --git a/src/zenflows/vf/recipe_process/type.ex b/src/zenflows/vf/recipe_process/type.ex @@ -37,6 +37,7 @@ for purposes of categorization. @process_conforms_to """ The standard specification or definition of a process. """ +@process_conforms_to_id "(`ProcesssSpecification`) #{@process_conforms_to}" @note "A textual description or comment." @desc "Specifies a process in a recipe for use in planning from recipe." @@ -60,10 +61,6 @@ object :recipe_process do field :note, :string end -object :recipe_process_response do - field :recipe_process, non_null(:recipe_process) -end - input_object :recipe_process_create_params do @desc @name field :name, non_null(:string) @@ -74,10 +71,7 @@ input_object :recipe_process_create_params do @desc @process_classified_as field :process_classified_as, list_of(non_null(:uri)) - # TODO: When - # https://github.com/absinthe-graphql/absinthe/issues/1126 results, - # apply the correct changes if any. - @desc "(`ProcessSpecification`) " <> @process_conforms_to + @desc @process_conforms_to_id field :process_conforms_to_id, non_null(:id), name: "process_conforms_to" @desc @note @@ -96,20 +90,40 @@ input_object :recipe_process_update_params do @desc @process_classified_as field :process_classified_as, list_of(non_null(:uri)) - @desc "(`ProcessSpecification`) " <> @process_conforms_to + @desc @process_conforms_to_id field :process_conforms_to_id, :id, name: "process_conforms_to" @desc @note field :note, :string end +object :recipe_process_response do + field :recipe_process, non_null(:recipe_process) +end + +object :recipe_process_edge do + field :cursor, non_null(:id) + field :node, non_null(:recipe_process) +end + +object :recipe_process_connection do + field :page_info, non_null(:page_info) + field :edges, non_null(list_of(non_null(:recipe_process_edge))) +end + object :query_recipe_process do field :recipe_process, :recipe_process do arg :id, non_null(:id) resolve &Resolv.recipe_process/2 end - #recipeProcesses(start: ID, limit: Int): [RecipeProcess!] + field :recipe_processes, :recipe_process_connection do + arg :first, :integer + arg :after, :id + arg :last, :integer + arg :before, :id + resolve &Resolv.recipe_processes/2 + end end object :mutation_recipe_process do diff --git a/test/vf/recipe_process/domain.test.exs b/test/vf/recipe_process/domain.test.exs @@ -26,69 +26,63 @@ alias Zenflows.VF.{ RecipeProcess.Domain, } -setup ctx do - params = %{ - name: Factory.uniq("name"), - note: Factory.uniq("note"), - process_classified_as: Factory.uniq_list("uri"), - process_conforms_to_id: Factory.insert!(:process_specification).id, - has_duration: Factory.build(:iduration), - } - - if ctx[:no_insert] do - %{params: params} - else - %{params: params, inserted: Factory.insert!(:recipe_process)} - end +setup do + %{ + params: %{ + name: Factory.uniq("name"), + note: Factory.uniq("note"), + process_classified_as: Factory.uniq_list("uri"), + process_conforms_to_id: Factory.insert!(:process_specification).id, + has_duration: Factory.build(:iduration), + }, + inserted: Factory.insert!(:recipe_process), + } end -test "by_id/1 returns a RecipeProcess", %{inserted: rec_proc} do - assert %RecipeProcess{} = Domain.by_id(rec_proc.id) +describe "one/1" do + test "with good id: finds the RecipeProcess", %{inserted: %{id: id}} do + assert {:ok, %RecipeProcess{}} = Domain.one(id) + end + + test "with bad id: doesn't find the RecipeProcess" do + assert {:error, "not found"} = Domain.one(Factory.id()) + end end describe "create/1" do - @tag :no_insert - test "creates a RecipeProcess with valid params without :has_duration", %{params: params} do - params = Map.delete(params, :has_duration) - - assert {:ok, %RecipeProcess{} = rec_proc} = Domain.create(params) - - assert rec_proc.name == params.name - assert rec_proc.note == params.note - assert rec_proc.process_classified_as == params.process_classified_as - assert rec_proc.process_conforms_to_id == params.process_conforms_to_id - assert rec_proc.has_duration_unit_type == nil - assert rec_proc.has_duration_numeric_duration == nil + test "with good params (with :has_duration): creates a RecipeProcess", %{params: params} do + assert {:ok, %RecipeProcess{} = new} = Domain.create(params) + assert new.name == params.name + assert new.note == params.note + assert new.process_classified_as == params.process_classified_as + assert new.process_conforms_to_id == params.process_conforms_to_id + assert new.has_duration_unit_type == params.has_duration.unit_type + assert new.has_duration_numeric_duration == params.has_duration.numeric_duration end - @tag :no_insert - test "creates a RecipeProcess with valid params with :has_duration", %{params: params} do - assert {:ok, %RecipeProcess{} = rec_proc} = Domain.create(params) - - assert rec_proc.name == params.name - assert rec_proc.note == params.note - assert rec_proc.process_classified_as == params.process_classified_as - assert rec_proc.process_conforms_to_id == params.process_conforms_to_id - assert rec_proc.has_duration_unit_type == params.has_duration.unit_type - assert rec_proc.has_duration_numeric_duration == params.has_duration.numeric_duration + test "with good params (without :has_duration): creates a RecipeProcess", %{params: params} do + params = Map.delete(params, :has_duration) + assert {:ok, %RecipeProcess{} = new} = Domain.create(params) + assert new.name == params.name + assert new.note == params.note + assert new.process_classified_as == params.process_classified_as + assert new.process_conforms_to_id == params.process_conforms_to_id + assert new.has_duration_unit_type == nil + assert new.has_duration_numeric_duration == nil end - @tag :no_insert - test "creates a RecipeProcess with valid params with :has_duration set to nil", %{params: params} do + test "with good params (with :has_duration set to nil): creates a RecipeProcess", %{params: params} do params = Map.put(params, :has_duration, nil) - - assert {:ok, %RecipeProcess{} = rec_proc} = Domain.create(params) - - assert rec_proc.name == params.name - assert rec_proc.note == params.note - assert rec_proc.process_classified_as == params.process_classified_as - assert rec_proc.process_conforms_to_id == params.process_conforms_to_id - assert rec_proc.has_duration_unit_type == nil - assert rec_proc.has_duration_numeric_duration == nil + assert {:ok, %RecipeProcess{} = new} = Domain.create(params) + assert new.name == params.name + assert new.note == params.note + assert new.process_classified_as == params.process_classified_as + assert new.process_conforms_to_id == params.process_conforms_to_id + assert new.has_duration_unit_type == nil + assert new.has_duration_numeric_duration == nil end - @tag :no_insert - test "doesn't create a RecipeProcess with valid invalid :has_duration fields", %{params: params} do + test "with bad params (with bad :has_duration): doesn't create a RecipeProcess", %{params: params} do params = Map.put(params, :has_duration, %{unit_type: nil, numeric_duration: nil}) assert {:error, %Changeset{errors: errs}} = Domain.create(params) assert length(Keyword.get_values(errs, :has_duration)) == 2 @@ -110,15 +104,14 @@ describe "create/1" do assert Keyword.has_key?(errs, :has_duration) end - test "doesn't create a RecipeProcess with invalid params" do + test "with bad params: doesn't create a RecipeProcess" do assert {:error, %Changeset{}} = Domain.create(%{}) end end describe "update/2" do - test "updates a RecipeProcess with valid params with :has_duration", %{params: params, inserted: old} do + test "with good params (with :has_duration): updates the RecipeProcess", %{params: params, inserted: old} do assert {:ok, %RecipeProcess{} = new} = Domain.update(old.id, params) - assert new.name == params.name assert new.note == params.note assert new.process_classified_as == params.process_classified_as @@ -127,11 +120,9 @@ describe "update/2" do assert new.has_duration_numeric_duration == params.has_duration.numeric_duration end - test "updates a RecipeProcess with valid params with :has_duration set to nil", %{params: params, inserted: old} do + test "with good params (with :has_duration set to nil): updates the RecipeProcess", %{params: params, inserted: old} do params = Map.put(params, :has_duration, nil) - assert {:ok, %RecipeProcess{} = new} = Domain.update(old.id, params) - assert new.name == params.name assert new.note == params.note assert new.process_classified_as == params.process_classified_as @@ -140,15 +131,14 @@ describe "update/2" do assert new.has_duration_unit_type == nil end - test "doesn't update a RecipeProcess with valid params with :has_duration fields set to nil", %{params: params, inserted: old} do + test "with bad params (with :has_duration fields set to nil): doesn't update the RecipeProcess", %{params: params, inserted: old} do params = Map.put(params, :has_duration, %{unit_type: nil, numeric_duration: nil}) assert {:error, %Changeset{errors: errs}} = Domain.update(old.id, params) assert length(Keyword.get_values(errs, :has_duration)) == 2 end - test "doesn't update a RecipeProcess", %{inserted: old} do + test "with bad params: doesn't update the RecipeProcess", %{inserted: old} do assert {:ok, %RecipeProcess{} = new} = Domain.update(old.id, %{}) - assert new.name == old.name assert new.note == old.note assert new.process_classified_as == old.process_classified_as @@ -158,9 +148,15 @@ describe "update/2" do end end -test "delete/1 deletes a RecipeProcess", %{inserted: %{id: id}} do - assert {:ok, %RecipeProcess{id: ^id}} = Domain.delete(id) - assert Domain.by_id(id) == nil +describe "delete/1" do + test "with good id: deletes the RecipeProcess", %{inserted: %{id: id}} do + assert {:ok, %RecipeProcess{id: ^id}} = Domain.delete(id) + assert {:error, "not found"} = Domain.one(id) + end + + test "with bad id: doesn't delete the RecipeProcess" do + assert {:error, "not found"} = Domain.delete(Factory.id()) + end end describe "preload/2" do diff --git a/test/vf/recipe_process/type.test.exs b/test/vf/recipe_process/type.test.exs @@ -21,156 +21,123 @@ use ZenflowsTest.Help.AbsinCase, async: true setup do %{ params: %{ - name: Factory.uniq("name"), - has_duration: Factory.build(:iduration), - process_classified_as: Factory.uniq_list("uri"), - process_conforms_to_id: Factory.insert!(:process_specification).id, - note: Factory.uniq("note"), + "name" => Factory.uniq("name"), + "hasDuration" => %{ + "unitType" => Factory.build(:time_unit) |> to_string(), + "numericDuration" => Factory.float(), + }, + "processClassifiedAs" => Factory.uniq_list("uri"), + "processConformsTo" => Factory.insert!(:process_specification).id, + "note" => Factory.uniq("note"), }, inserted: Factory.insert!(:recipe_process), } end +@frag """ +fragment recipeProcess on RecipeProcess { + id + name + note + processClassifiedAs + processConformsTo {id} + hasDuration { + unitType + numericDuration + } +} +""" + describe "Query" do - test "recipeProcess()", %{inserted: rec_proc} do + test "recipeProcess", %{inserted: new} do assert %{data: %{"recipeProcess" => data}} = - query!(""" - recipeProcess(id: "#{rec_proc.id}") { - id - name - note - processClassifiedAs - processConformsTo { id } - hasDuration { - unitType - numericDuration - } + run!(""" + #{@frag} + query ($id: ID!) { + recipeProcess(id: $id) {...recipeProcess} } - """) + """, vars: %{"id" => new.id}) - assert data["id"] == rec_proc.id - assert data["name"] == rec_proc.name - assert data["note"] == rec_proc.note - assert data["processClassifiedAs"] == rec_proc.process_classified_as - assert data["processConformsTo"]["id"] == rec_proc.process_conforms_to_id - assert data["hasDuration"]["unitType"] == to_string(rec_proc.has_duration_unit_type) - assert data["hasDuration"]["numericDuration"] == rec_proc.has_duration_numeric_duration + assert data["id"] == new.id + assert data["name"] == new.name + assert data["note"] == new.note + assert data["processClassifiedAs"] == new.process_classified_as + assert data["processConformsTo"]["id"] == new.process_conforms_to_id + assert data["hasDuration"]["unitType"] == to_string(new.has_duration_unit_type) + assert data["hasDuration"]["numericDuration"] == new.has_duration_numeric_duration end end describe "Mutation" do - test "createRecipeProcess()", %{params: params} do + test "createRecipeProcess", %{params: params} do assert %{data: %{"createRecipeProcess" => %{"recipeProcess" => data}}} = - mutation!(""" - createRecipeProcess(recipeProcess: { - name: "#{params.name}" - note: "#{params.note}" - processClassifiedAs: #{inspect(params.process_classified_as)} - processConformsTo: "#{params.process_conforms_to_id}" - hasDuration: { - unitType: #{params.has_duration.unit_type} - numericDuration: #{params.has_duration.numeric_duration} - } - }) { - recipeProcess { - id - name - note - processClassifiedAs - processConformsTo { id } - hasDuration { - unitType - numericDuration - } + run!(""" + #{@frag} + mutation ($recipeProcess: RecipeProcessCreateParams!) { + createRecipeProcess(recipeProcess: $recipeProcess) { + recipeProcess {...recipeProcess} } } - """) + """, vars: %{"recipeProcess" => params}) assert {:ok, _} = Zenflows.DB.ID.cast(data["id"]) - assert data["name"] == params.name - assert data["note"] == params.note - assert data["processClassifiedAs"] == params.process_classified_as - assert data["processConformsTo"]["id"] == params.process_conforms_to_id - assert data["hasDuration"]["unitType"] == to_string(params.has_duration.unit_type) - assert data["hasDuration"]["numericDuration"] == params.has_duration.numeric_duration + assert data["name"] == params["name"] + assert data["note"] == params["note"] + assert data["processClassifiedAs"] == params["processClassifiedAs"] + assert data["processConformsTo"]["id"] == params["processConformsTo"] + assert data["hasDuration"] == params["hasDuration"] end - test "updateRecipeProcess()", %{params: params, inserted: rec_proc} do + test "updateRecipeProcess", %{params: params, inserted: old} do assert %{data: %{"updateRecipeProcess" => %{"recipeProcess" => data}}} = - mutation!(""" - updateRecipeProcess(recipeProcess: { - id: "#{rec_proc.id}" - name: "#{params.name}" - note: "#{params.note}" - processClassifiedAs: #{inspect(params.process_classified_as)} - processConformsTo: "#{params.process_conforms_to_id}" - hasDuration: { - unitType: #{params.has_duration.unit_type} - numericDuration: #{params.has_duration.numeric_duration} - } - }) { - recipeProcess { - id - name - note - processClassifiedAs - processConformsTo { id } - hasDuration { - unitType - numericDuration - } + run!(""" + #{@frag} + mutation ($recipeProcess: RecipeProcessUpdateParams!) { + updateRecipeProcess(recipeProcess: $recipeProcess) { + recipeProcess {...recipeProcess} } } - """) + """, vars: %{"recipeProcess" => Map.put(params, "id", old.id)}) - assert data["id"] == rec_proc.id - assert data["name"] == params.name - assert data["note"] == params.note - assert data["processClassifiedAs"] == params.process_classified_as - assert data["processConformsTo"]["id"] == params.process_conforms_to_id - assert data["hasDuration"]["unitType"] == to_string(params.has_duration.unit_type) - assert data["hasDuration"]["numericDuration"] == params.has_duration.numeric_duration + assert data["id"] == old.id + assert data["name"] == params["name"] + assert data["note"] == params["note"] + assert data["processClassifiedAs"] == params["processClassifiedAs"] + assert data["processConformsTo"]["id"] == params["processConformsTo"] + assert data["hasDuration"] == params["hasDuration"] end - test "updateRecipeProcess(), set hasDuration to null", %{params: params, inserted: rec_proc} do + test "updateRecipeProcess: hasDuration set null", %{params: params, inserted: old} do assert %{data: %{"updateRecipeProcess" => %{"recipeProcess" => data}}} = - mutation!(""" - updateRecipeProcess(recipeProcess: { - id: "#{rec_proc.id}" - name: "#{params.name}" - note: "#{params.note}" - processClassifiedAs: #{inspect(params.process_classified_as)} - processConformsTo: "#{params.process_conforms_to_id}" - hasDuration: null - }) { - recipeProcess { - id - name - note - processClassifiedAs - processConformsTo { id } - hasDuration { - unitType - numericDuration - } + run!(""" + #{@frag} + mutation ($recipeProcess: RecipeProcessUpdateParams!) { + updateRecipeProcess(recipeProcess: $recipeProcess) { + recipeProcess {...recipeProcess} } } - """) + """, vars: %{ + "recipeProcess" => + params + |> Map.put("id", old.id) + |> Map.put("hasDuration", nil) + }) - assert data["id"] == rec_proc.id - assert data["name"] == params.name - assert data["note"] == params.note - assert data["processClassifiedAs"] == params.process_classified_as - assert data["processConformsTo"]["id"] == params.process_conforms_to_id - assert data["hasDuration"]["unitType"] == nil - assert data["hasDuration"]["numericDuration"] == nil + assert data["id"] == old.id + assert data["name"] == params["name"] + assert data["note"] == params["note"] + assert data["processClassifiedAs"] == params["processClassifiedAs"] + assert data["processConformsTo"]["id"] == params["processConformsTo"] + assert data["hasDuration"] == nil end - test "deleteRecipeProcess()", %{inserted: %{id: id}} do + test "deleteRecipeProcess", %{inserted: %{id: id}} do assert %{data: %{"deleteRecipeProcess" => true}} = - mutation!(""" - deleteRecipeProcess(id: "#{id}") - """) + run!(""" + mutation ($id: ID!) { + deleteRecipeProcess(id: $id) + } + """, vars: %{"id" => id}) end end end