zf

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

commit 308fcdf722d3ae8bfacfedf0e4573927da975672
parent 3047717966d639b19549a0a901078c5a9bd7473b
Author: srfsh <dev@srf.sh>
Date:   Tue, 23 Aug 2022 11:32:06 +0300

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

Diffstat:
Msrc/zenflows/vf/scenario/domain.ex | 47++++++++++++++++++++++++-----------------------
Msrc/zenflows/vf/scenario/resolv.ex | 20++++++++++++--------
Msrc/zenflows/vf/scenario/type.ex | 48+++++++++++++++++++++++++++---------------------
Mtest/vf/scenario/domain.test.exs | 73+++++++++++++++++++++++++++++++++++++++----------------------------------
Mtest/vf/scenario/type.test.exs | 140++++++++++++++++++++++++++++++++++---------------------------------------------
5 files changed, 162 insertions(+), 166 deletions(-)

diff --git a/src/zenflows/vf/scenario/domain.ex b/src/zenflows/vf/scenario/domain.ex @@ -20,17 +20,28 @@ defmodule Zenflows.VF.Scenario.Domain do alias Ecto.Multi alias Zenflows.DB.Repo +alias Zenflows.GQL.Paging alias Zenflows.VF.Scenario @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()) :: Scenario.t() | nil -def by_id(repo \\ Repo, id) do - repo.get(Scenario, id) +@spec one(repo(), id() | map() | Keyword.t()) + :: {:ok, Scenario.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(Scenario, clauses) do + nil -> {:error, "not found"} + found -> {:ok, found} + end +end + +@spec all(Paging.params()) :: Paging.result(Scenario.t()) +def all(params) do + Paging.page(Scenario, params) end @spec create(params()) :: {:ok, Scenario.t()} | {:error, chgset()} @@ -44,11 +55,13 @@ def create(params) do end end -@spec update(id(), params()) :: {:ok, Scenario.t()} | {:error, chgset()} +@spec update(id(), params()) + :: {:ok, Scenario.t()} | {:error, String.t() | chgset()} def update(id, params) do Multi.new() - |> Multi.run(:get, multi_get(id)) - |> Multi.update(:update, &Scenario.chgset(&1.get, params)) + |> Multi.put(:id, id) + |> Multi.run(:one, &one/2) + |> Multi.update(:update, &Scenario.chgset(&1.one, params)) |> Repo.transaction() |> case do {:ok, %{update: s}} -> {:ok, s} @@ -56,11 +69,12 @@ def update(id, params) do end end -@spec delete(id()) :: {:ok, Scenario.t()} | {:error, chgset()} +@spec delete(id()) :: {:ok, Scenario.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: s}} -> {:ok, s} @@ -76,17 +90,4 @@ end def preload(scen, :refinement_of) do Repo.preload(scen, :refinement_of) end - -# Returns a Scenario 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, Scenario.t()} | {:error, String.t()}) -defp multi_get(id) do - fn repo, _ -> - case by_id(repo, id) do - nil -> {:error, "not found"} - s -> {:ok, s} - end - end -end end diff --git a/src/zenflows/vf/scenario/resolv.ex b/src/zenflows/vf/scenario/resolv.ex @@ -20,36 +20,40 @@ defmodule Zenflows.VF.Scenario.Resolv do use Absinthe.Schema.Notation -alias Zenflows.VF.{Scenario, Scenario.Domain} +alias Zenflows.VF.Scenario.Domain -def scenario(%{id: id}, _info) do - {:ok, Domain.by_id(id)} +def scenario(params, _) do + Domain.one(params) end -def create_scenario(%{scenario: params}, _info) do +def scenarios(params, _) do + Domain.all(params) +end + +def create_scenario(%{scenario: params}, _) do with {:ok, scen} <- Domain.create(params) do {:ok, %{scenario: scen}} end end -def update_scenario(%{scenario: %{id: id} = params}, _info) do +def update_scenario(%{scenario: %{id: id} = params}, _) do with {:ok, scen} <- Domain.update(id, params) do {:ok, %{scenario: scen}} end end -def delete_scenario(%{id: id}, _info) do +def delete_scenario(%{id: id}, _) do with {:ok, _} <- Domain.delete(id) do {:ok, true} end end -def defined_as(%Scenario{} = scen, _args, _info) do +def defined_as(scen, _, _) do scenario = Domain.preload(scen, :defined_as) {:ok, scenario.defined_as} end -def refinement_of(%Scenario{} = scen, _args, _info) do +def refinement_of(scen, _, _) do scenario = Domain.preload(scen, :refinement_of) {:ok, scenario.refinement_of} end diff --git a/src/zenflows/vf/scenario/type.ex b/src/zenflows/vf/scenario/type.ex @@ -38,10 +38,12 @@ period. @defined_as """ The scenario definition for this scenario, for example yearly budget. """ +@defined_as_id "(`ScenarioDefinition`) #{@defined_as}" @refinement_of """ This scenario refines another scenario, often as time moves closer or for more detail. """ +@refinement_of_id "(`Scenario`) #{@refinement_of}" @desc """ An estimated or analytical logical collection of higher level processes @@ -70,10 +72,6 @@ object :scenario do field :refinement_of, :scenario, resolve: &Resolv.refinement_of/3 end -object :scenario_response do - field :scenario, non_null(:scenario) -end - input_object :scenario_create_params do @desc @name field :name, non_null(:string) @@ -87,16 +85,10 @@ input_object :scenario_create_params do @desc @has_end field :has_end, :datetime - # TODO: When - # https://github.com/absinthe-graphql/absinthe/issues/1126 results, - # apply the correct changes if any. - @desc "(`ScenarioDefinition`) " <> @defined_as + @desc @defined_as_id field :defined_as_id, :id, name: "defined_as" - # TODO: When - # https://github.com/absinthe-graphql/absinthe/issues/1126 results, - # apply the correct changes if any. - @desc "(`Scenario`) " <> @refinement_of + @desc @refinement_of_id field :refinement_of_id, :id, name: "refinement_of" end @@ -115,26 +107,40 @@ input_object :scenario_update_params do @desc @has_end field :has_end, :datetime - # TODO: When - # https://github.com/absinthe-graphql/absinthe/issues/1126 results, - # apply the correct changes if any. - @desc "(`ScenarioDefinition`) " <> @defined_as + @desc @defined_as_id field :defined_as_id, :id, name: "defined_as" - # TODO: When - # https://github.com/absinthe-graphql/absinthe/issues/1126 results, - # apply the correct changes if any. - @desc "(`Scenario`) " <> @refinement_of + @desc @refinement_of_id field :refinement_of_id, :id, name: "refinement_of" end +object :scenario_response do + field :scenario, non_null(:scenario) +end + +object :scenario_edge do + field :cursor, non_null(:id) + field :node, non_null(:scenario) +end + +object :scenario_connection do + field :page_info, non_null(:page_info) + field :edges, non_null(list_of(non_null(:scenario_edge))) +end + object :query_scenario do field :scenario, :scenario do arg :id, non_null(:id) resolve &Resolv.scenario/2 end - #scenarioDefinitions(start: ID, limit: Int): [Scenario!] + field :scenarios, :scenario_connection do + arg :first, :integer + arg :after, :id + arg :last, :integer + arg :before, :id + resolve &Resolv.scenarios/2 + end end object :mutation_scenario do diff --git a/test/vf/scenario/domain.test.exs b/test/vf/scenario/domain.test.exs @@ -25,49 +25,49 @@ alias Zenflows.VF.{ ScenarioDefinition, } -setup ctx do - params = %{ - name: Factory.uniq("name"), - note: Factory.uniq("note"), - has_beginning: DateTime.utc_now(), - has_end: DateTime.utc_now(), - defined_as_id: Factory.insert!(:scenario_definition).id, - refinement_of_id: Factory.insert!(:scenario).id, - } +setup do + %{ + params: %{ + name: Factory.uniq("name"), + note: Factory.uniq("note"), + has_beginning: DateTime.utc_now(), + has_end: DateTime.utc_now(), + defined_as_id: Factory.insert!(:scenario_definition).id, + refinement_of_id: Factory.insert!(:scenario).id, + }, + inserted: Factory.insert!(:scenario), + } +end - if ctx[:no_insert] do - %{params: params} - else - %{params: params, inserted: Factory.insert!(:scenario)} +describe "one/1" do + test "with good id: finds the Scenario", %{inserted: %{id: id}} do + assert {:ok, %Scenario{}} = Domain.one(id) end -end -test "by_id/1 returns a Scenario", %{inserted: scen} do - assert %Scenario{} = Domain.by_id(scen.id) + test "with bad id: doesn't find the Scenario" do + assert {:error, "not found"} = Domain.one(Factory.id()) + end end describe "create/1" do - @tag :no_insert - test "creates a Scenario with valid params", %{params: params} do - assert {:ok, %Scenario{} = scen} = Domain.create(params) - - assert scen.name == params.name - assert scen.note == params.note - assert scen.has_beginning == params.has_beginning - assert scen.has_end == params.has_end - assert scen.defined_as_id == params.defined_as_id - assert scen.refinement_of_id == params.refinement_of_id + test "with good params: creates a Scenario", %{params: params} do + assert {:ok, %Scenario{} = new} = Domain.create(params) + assert new.name == params.name + assert new.note == params.note + assert new.has_beginning == params.has_beginning + assert new.has_end == params.has_end + assert new.defined_as_id == params.defined_as_id + assert new.refinement_of_id == params.refinement_of_id end - test "doesn't create a Scenario with invalid params" do + test "with bad params: doesn't create a Scenario" do assert {:error, %Changeset{}} = Domain.create(%{}) end end describe "update/2" do - test "updates a Scenario with valid params", %{params: params, inserted: old} do + test "with good params: updates the Scenario", %{params: params, inserted: old} do assert {:ok, %Scenario{} = new} = Domain.update(old.id, params) - assert new.name == params.name assert new.note == params.note assert new.has_beginning == params.has_beginning @@ -76,9 +76,8 @@ describe "update/2" do assert new.refinement_of_id == params.refinement_of_id end - test "doesn't update a Scenario", %{inserted: old} do + test "with bad params: doesn't update the Scenario", %{inserted: old} do assert {:ok, %Scenario{} = new} = Domain.update(old.id, %{}) - assert new.name == old.name assert new.note == old.note assert new.has_beginning == old.has_beginning @@ -88,9 +87,15 @@ describe "update/2" do end end -test "delete/1 deletes a Scenario", %{inserted: %{id: id}} do - assert {:ok, %Scenario{id: ^id}} = Domain.delete(id) - assert Domain.by_id(id) == nil +describe "delete/1" do + test "with good id: deletes the Scenario", %{inserted: %{id: id}} do + assert {:ok, %Scenario{id: ^id}} = Domain.delete(id) + assert {:error, "not found"} = Domain.one(id) + end + + test "with bad id: doesn't delete the Scenario" do + assert {:error, "not found"} = Domain.delete(Factory.id()) + end end describe "preload/2" do diff --git a/test/vf/scenario/type.test.exs b/test/vf/scenario/type.test.exs @@ -21,113 +21,93 @@ use ZenflowsTest.Help.AbsinCase, async: true setup do %{ params: %{ - name: Factory.uniq("name"), - note: Factory.uniq("note"), - has_beginning: DateTime.utc_now(), - has_end: DateTime.utc_now(), - defined_as_id: Factory.insert!(:scenario_definition).id, - refinement_of_id: Factory.insert!(:scenario).id, + "name" => Factory.str("name"), + "note" => Factory.str("note"), + "hasBeginning" => Factory.iso_now(), + "hasEnd" => Factory.iso_now(), + "definedAs" => Factory.insert!(:scenario_definition).id, + "refinementOf" => Factory.insert!(:scenario).id, }, inserted: Factory.insert!(:scenario), } end +@frag """ +fragment scenario on Scenario { + id + name + note + hasBeginning + hasEnd + definedAs {id} + refinementOf {id} +} +""" + describe "Query" do - test "scenario()", %{inserted: scen} do + test "scenario", %{inserted: new} do assert %{data: %{"scenario" => data}} = - query!(""" - scenario(id: "#{scen.id}") { - id - name - note - hasBeginning - hasEnd - definedAs {id} - refinementOf {id} + run!(""" + #{@frag} + query ($id: ID!) { + scenario(id: $id) {...scenario} } - """) + """, vars: %{"id" => new.id}) - assert data["id"] == scen.id - assert data["name"] == scen.name - assert data["note"] == scen.note - assert data["hasBeginning"] == DateTime.to_iso8601(scen.has_beginning) - assert data["hasEnd"] == DateTime.to_iso8601(scen.has_end) - assert data["definedAs"]["id"] == scen.defined_as_id - assert data["refinementOf"]["id"] == scen.refinement_of_id + assert data["id"] == new.id + assert data["name"] == new.name + assert data["note"] == new.note + assert data["hasBeginning"] == DateTime.to_iso8601(new.has_beginning) + assert data["hasEnd"] == DateTime.to_iso8601(new.has_end) + assert data["definedAs"]["id"] == new.defined_as_id + assert data["refinementOf"]["id"] == new.refinement_of_id end end describe "Mutation" do - test "createScenario()", %{params: params} do + test "createScenario", %{params: params} do assert %{data: %{"createScenario" => %{"scenario" => data}}} = - mutation!(""" - createScenario(scenario: { - name: "#{params.name}" - note: "#{params.note}" - hasBeginning: "#{params.has_beginning}" - hasEnd: "#{params.has_end}" - definedAs: "#{params.defined_as_id}" - refinementOf: "#{params.refinement_of_id}" - }) { - scenario { - id - name - note - hasBeginning - hasEnd - definedAs {id} - refinementOf {id} + run!(""" + #{@frag} + mutation ($scenario: ScenarioCreateParams!) { + createScenario(scenario: $scenario) { + scenario {...scenario} } } - """) + """, vars: %{"scenario" => params}) assert {:ok, _} = Zenflows.DB.ID.cast(data["id"]) - assert data["name"] == params.name - assert data["note"] == params.note - assert data["hasBeginning"] == DateTime.to_iso8601(params.has_beginning) - assert data["hasEnd"] == DateTime.to_iso8601(params.has_end) - assert data["definedAs"]["id"] == params.defined_as_id - assert data["refinementOf"]["id"] == params.refinement_of_id + keys = ~w[name note hasBeginning hasEnd] + assert Map.take(data, keys) == Map.take(params, keys) + assert data["definedAs"]["id"] == params["definedAs"] + assert data["refinementOf"]["id"] == params["refinementOf"] end - test "updateScenario()", %{params: params, inserted: scen} do + test "updateScenario", %{params: params, inserted: old} do assert %{data: %{"updateScenario" => %{"scenario" => data}}} = - mutation!(""" - updateScenario(scenario: { - id: "#{scen.id}" - name: "#{params.name}" - note: "#{params.note}" - hasBeginning: "#{params.has_beginning}" - hasEnd: "#{params.has_end}" - definedAs: "#{params.defined_as_id}" - refinementOf: "#{params.refinement_of_id}" - }) { - scenario { - id - name - note - hasBeginning - hasEnd - definedAs {id} - refinementOf {id} + run!(""" + #{@frag} + mutation ($scenario: ScenarioUpdateParams!) { + updateScenario(scenario: $scenario) { + scenario {...scenario} } } - """) + """, vars: %{"scenario" => Map.put(params, "id", old.id)}) - assert data["id"] == scen.id - assert data["name"] == params.name - assert data["note"] == params.note - assert data["hasBeginning"] == DateTime.to_iso8601(params.has_beginning) - assert data["hasEnd"] == DateTime.to_iso8601(params.has_end) - assert data["definedAs"]["id"] == params.defined_as_id - assert data["refinementOf"]["id"] == params.refinement_of_id + assert data["id"] == old.id + keys = ~w[name note hasBeginning hasEnd] + assert Map.take(data, keys) == Map.take(params, keys) + assert data["definedAs"]["id"] == params["definedAs"] + assert data["refinementOf"]["id"] == params["refinementOf"] end - test "deleteScenario()", %{inserted: %{id: id}} do + test "deleteScenario", %{inserted: %{id: id}} do assert %{data: %{"deleteScenario" => true}} = - mutation!(""" - deleteScenario(id: "#{id}") - """) + run!(""" + mutation ($id: ID!) { + deleteScenario(id: $id) + } + """, vars: %{"id" => id}) end end end