Test auto_sync ignore case

* Test case: auto_sync is disabled.

* Test case: auto_sync of Device assets

* Test case: auto_sync of FbosConfig

* Test case: auto_sync of FbosConfig assets

* Test case: auto_sync of FarmwareInstallation assets

Test case: `cache_sync/3`
pull/974/head
Rick Carlino 2019-05-15 09:10:54 -05:00 committed by Connor Rigby
parent 6f49345477
commit c3819359b0
No known key found for this signature in database
GPG Key ID: 29A88B24B70456E0
4 changed files with 230 additions and 101 deletions

View File

@ -2,21 +2,68 @@ defmodule FarmbotCore.Asset.Command do
@moduledoc """
A collection of functions that _write_ to the DB
"""
require Logger
alias FarmbotCore.{
Asset,
Asset.Repo,
Asset.Device
}
alias FarmbotCore.{Asset, Asset.Repo, Asset.Device}
alias FarmbotExt.API.{EagerLoader}
@type kind :: String.t()
@type params :: map()
@type id :: float()
@type return_type :: :ok
@type kind :: Device | FbosConfig | FwConfig
@type return_type ::
Device.t() | FbosConfig.t() | FwConfig.t()
@callback update(kind, params, id) :: return_type
def update("Device", params) do
Asset.device() |> Device.changeset(params) |> Repo.update!()
end
@callback update(kind, params :: map()) :: return_type
def update(Device, params) do
Asset.device()
|> Device.changeset(params)
|> Repo.update!()
def update("FbosConfig", params, _),
do: Asset.update_fbos_config!(params)
def update("FirmwareConfig", params, _),
do: Asset.update_firmware_config!(params)
def update("FarmwareEnv", params, id),
do: Asset.upsert_farmware_env_by_id(id, params)
def update("FarmwareInstallation", id, params),
do: Asset.upsert_farmware_env_by_id(id, params)
# Deletion use case:
def update(asset_kind, nil, id) do
old = Repo.get_by(as_module(asset_kind), id: id)
old && Repo.delete!(old)
:ok
end
# Catch-all use case:
def update(asset_kind, params, id) do
Logger.info("autosyncing: #{asset_kind} #{id} #{inspect(params)}")
case Repo.get_by(as_module(asset_kind), id: id) do
nil ->
struct(asset_kind)
|> asset_kind.changeset(params)
|> Repo.insert!()
asset ->
asset_kind.changeset(asset, params)
|> Repo.update!()
end
:ok
end
@callback cached_update(kind, params, id) :: return_type
def cached_update(asset_kind, params, id) do
mod = as_module(asset_kind)
asset = Repo.get_by(mod, id: id) || struct(asset_kind)
changeset = mod.changeset(asset, params)
:ok = EagerLoader.cache(changeset)
:ok = BotState.set_sync_status("sync_now")
end
# Convert string `"Device"` to module `Asset.Device`
defp as_module(asset_kind) do
Module.concat([Asset, asset_kind])
end
end

View File

@ -1,5 +1,4 @@
defmodule FarmbotCore.Asset.Query do
alias FarmbotCore.Asset
@callback auto_sync?() :: boolean()
def auto_sync?() do
Asset.fbos_config().auto_sync

View File

@ -10,21 +10,12 @@ defmodule FarmbotExt.AMQP.AutoSyncChannel do
alias FarmbotCore.BotState
alias FarmbotExt.AMQP.ConnectionWorker
alias FarmbotExt.API.{Preloader, EagerLoader}
alias FarmbotExt.API.{Preloader}
require Logger
require FarmbotCore.Logger
alias FarmbotCore.{
Asset,
Asset.Device,
Asset.FarmwareEnv,
Asset.FarmwareInstallation,
Asset.FbosConfig,
Asset.FirmwareConfig,
Asset.Repo,
JSON
}
alias FarmbotCore.{Asset, JSON}
@known_kinds ~w(
Device
@ -43,6 +34,13 @@ defmodule FarmbotExt.AMQP.AutoSyncChannel do
Tool
)
@cache_kinds ~w(
Device
FbosConfig
FirmwareConfig
FarmwareEnv
FarmwareInstallation
)
defstruct [:conn, :chan, :jwt, :preloaded]
alias __MODULE__, as: State
@ -105,7 +103,6 @@ defmodule FarmbotExt.AMQP.AutoSyncChannel do
case String.split(key, ".") do
["bot", ^device, "sync", asset_kind, id_str] when asset_kind in @known_kinds ->
asset_kind = Module.concat([Asset, asset_kind])
id = data["id"] || String.to_integer(id_str)
handle_asset(asset_kind, id, body)
@ -117,6 +114,63 @@ defmodule FarmbotExt.AMQP.AutoSyncChannel do
{:noreply, state}
end
def handle_asset(asset_kind, id, params) do
auto_sync? = query().auto_sync?()
cond do
# TODO(Connor) no way to cache a deletion yet
is_nil(params) && !auto_sync? ->
:ok
asset_kind == Device ->
command().update(asset_kind, params)
:ok
asset_kind == FbosConfig ->
command().update(asset_kind, params)
:ok
asset_kind == FirmwareConfig ->
Asset.update_firmware_config!(params)
:ok
# TODO(Connor) make this use `sync_group0()`
asset_kind == FarmwareEnv ->
Asset.upsert_farmware_env_by_id(id, params)
:ok
# TODO(Connor) make this use `sync_group0()`
asset_kind == FarmwareInstallation ->
Asset.upsert_farmware_manifest_by_id(id, params)
:ok
is_nil(params) && auto_sync? ->
old = Repo.get_by(asset_kind, id: id)
old && Repo.delete!(old)
:ok
auto_sync? ->
case Repo.get_by(asset_kind, id: id) do
nil ->
struct(asset_kind)
|> asset_kind.changeset(params)
|> Repo.insert!()
asset ->
asset_kind.changeset(asset, params)
|> Repo.update!()
end
:ok
true ->
asset = Repo.get_by(asset_kind, id: id) || struct(asset_kind)
changeset = asset_kind.changeset(asset, params)
:ok = EagerLoader.cache(changeset)
:ok = BotState.set_sync_status("sync_now")
end
end
def handle_call(:network_status, _, state) do
{
:reply,
@ -130,85 +184,25 @@ defmodule FarmbotExt.AMQP.AutoSyncChannel do
end
def handle_asset(asset_kind, id, params) do
auto_sync? = query().auto_sync?()
if auto_sync? do
if query().auto_sync?() do
:ok = BotState.set_sync_status("syncing")
auto_sync(asset_kind, id, params)
command().update(asset_kind, params, id)
:ok = BotState.set_sync_status("synced")
else
cache_sync(asset_kind, id, params)
cache_sync(asset_kind, params, id)
end
end
def auto_sync(asset_kind = Device, _id, params) do
# TODO(Connor) maybe check this value?
_ = command().update(asset_kind, params)
:ok
end
def auto_sync(FbosConfig, _id, params) do
_ = Asset.update_fbos_config!(params)
:ok
end
def auto_sync(FirmwareConfig, _id, params) do
_ = Asset.update_firmware_config!(params)
:ok
end
def auto_sync(FarmwareEnv, id, params) do
_ = Asset.upsert_farmware_env_by_id(id, params)
:ok
end
def auto_sync(FarmwareInstallation, id, params) do
_ = Asset.upsert_farmware_env_by_id(id, params)
:ok
end
def auto_sync(asset_kind, id, nil) do
old = Repo.get_by(asset_kind, id: id)
old && Repo.delete!(old)
:ok
end
def auto_sync(asset_kind, id, params) do
Logger.info("autosyncing: #{asset_kind} #{id} #{inspect(params)}")
case Repo.get_by(asset_kind, id: id) do
nil ->
struct(asset_kind)
|> asset_kind.changeset(params)
|> Repo.insert!()
asset ->
asset_kind.changeset(asset, params)
|> Repo.update!()
end
:ok
end
def cache_sync(kind, id, params)
when kind in [
Device,
FbosConfig,
FirmwareConfig,
FarmwareEnv,
FarmwareInstallation
] do
def cache_sync(kind, params, id) when kind in @cache_kinds do
:ok = BotState.set_sync_status("syncing")
:ok = auto_sync(kind, id, params)
:ok = command().update(kind, params, id)
:ok = BotState.set_sync_status("synced")
end
def cache_sync(asset_kind, id, params) do
Logger.info("Autcaching sync #{asset_kind} #{id} #{inspect(params)}")
asset = Repo.get_by(asset_kind, id: id) || struct(asset_kind)
changeset = asset_kind.changeset(asset, params)
:ok = EagerLoader.cache(changeset)
:ok = BotState.set_sync_status("sync_now")
def cache_sync(asset_kind, params, id) do
Logger.info("Autocaching sync #{asset_kind} #{id} #{inspect(params)}")
command().cached_update(asset_kind, params, id)
end
defp compute_reply_from_amqp_state(state, %{conn: conn, chan: chan}) do

View File

@ -42,7 +42,7 @@ defmodule FarmbotExt.AMQP.AutoSyncChannelTest do
stub(MockConnectionWorker, :close_channel, fn _ ->
send(test_pid, :close_channel_called)
nil
:ok
end)
stub(MockConnectionWorker, :rpc_reply, fn chan, jwt_dot_bot, label ->
@ -94,7 +94,7 @@ defmodule FarmbotExt.AMQP.AutoSyncChannelTest do
assert real_conn == fake_con
assert is_preloaded
send(pid, {:basic_cancel, "--NOT USED--"})
assert_receive :close_channel_called, 75
assert_receive :close_channel_called, 150
end
test "catch-all clause for inbound AMQP messages" do
@ -135,12 +135,101 @@ defmodule FarmbotExt.AMQP.AutoSyncChannelTest do
key = "bot.device_15.sync.Device.999"
stub(MockQuery, :auto_sync?, fn -> true end)
stub(MockCommand, :update, fn x, y ->
send(test_pid, {:update_called, x, y})
nil
stub(MockCommand, :update, fn x, y, z ->
send(test_pid, {:update_called, x, y, z})
:ok
end)
send(pid, {:basic_deliver, payload, %{routing_key: key}})
assert_receive {:update_called, FarmbotCore.Asset.Device, %{}}, 10
assert_receive {:update_called, "Device", %{}, 999}, 10
end
def simple_asset_test_singleton(module_name) do
%{pid: pid} = under_normal_conditions()
test_pid = self()
payload = '{"args":{"label":"foo"},"body":{"foo": "bar"}}'
key = "bot.device_15.sync.#{module_name}.999"
stub(MockQuery, :auto_sync?, fn -> true end)
stub(MockCommand, :update, fn x, y, z ->
send(test_pid, {:update_called, x, y, z})
:ok
end)
stub(MockCommand, :update, fn x, y, z ->
send(test_pid, {:update_called, x, y, z})
:ok
end)
send(pid, {:basic_deliver, payload, %{routing_key: key}})
assert_receive {:update_called, module_name, %{"foo" => "bar"}, 999}, 10
end
def simple_asset_test_plural(module_name) do
%{pid: pid} = under_normal_conditions()
test_pid = self()
payload = '{"args":{"label":"foo"},"body":{"foo": "bar"}}'
key = "bot.device_15.sync.#{module_name}.999"
stub(MockQuery, :auto_sync?, fn -> true end)
stub(MockCommand, :update, fn x, y, z ->
send(test_pid, {:update_called, x, y, z})
:ok
end)
send(pid, {:basic_deliver, payload, %{routing_key: key}})
assert_receive {:update_called, module_name, %{"foo" => "bar"}, 999}, 10
end
test "handles FbosConfig", do: simple_asset_test_singleton("FbosConfig")
test "handles FirmwareConfig", do: simple_asset_test_singleton("FirmwareConfig")
test "handles FarmwareEnv", do: simple_asset_test_plural("FarmwareEnv")
test "handles FarmwareInstallation", do: simple_asset_test_plural("FarmwareInstallation")
test "handles auto_sync of 'cache_assets' when auto_sync is false" do
test_pid = self()
%{pid: pid} = under_normal_conditions()
key = "bot.device_15.sync.FbosConfig.999"
payload = '{"args":{"label":"foo"},"body":{"foo": "bar"}}'
stub(MockQuery, :auto_sync?, fn ->
send(test_pid, :called_auto_sync?)
false
end)
stub(MockCommand, :update, fn x, y, z ->
send(test_pid, {:update_called, x, y, z})
:ok
end)
send(pid, {:basic_deliver, payload, %{routing_key: key}})
assert_receive :called_auto_sync?, 10
assert_receive {:update_called, "FbosConfig", %{"foo" => "bar"}, 999}, 10
end
test "auto_sync disabled, resource not in @cache_kinds" do
test_pid = self()
%{pid: pid} = under_normal_conditions()
key = "bot.device_15.sync.Point.999"
payload = '{"args":{"label":"foo"},"body":{"foo": "bar"}}'
stub(MockQuery, :auto_sync?, fn ->
send(test_pid, :called_auto_sync?)
false
end)
stub(MockCommand, :cached_update, fn x, y, z ->
send(test_pid, {:cached_update_called, x, y, z})
:ok
end)
send(pid, {:basic_deliver, payload, %{routing_key: key}})
assert_receive {:cached_update_called, "Point", %{"foo" => "bar"}, 999}, 10
end
end