Merge pull request #1168 from FarmBot/criteria_groups_cleanup
FarmbotExt Test Suite Cleanuppull/1169/head
commit
2f4b0a5754
|
@ -0,0 +1,10 @@
|
|||
# Jan - Mar 2020
|
||||
|
||||
| Project | Jan 1 20 | Feb 6 20 | Mar 4 20 |STATUS|
|
||||
|-----------------------|----------|----------|----------|------|
|
||||
| farmbot_celery_script | 53.7% | 54.0% | 54.0% |OK |
|
||||
| farmbot_core | 22.2% | 19.8% | 26.3% |OK |
|
||||
| farmbot_ext | 53.6% | 52.7% | 38.1% |FIX | !!!
|
||||
| farmbot_firmware | 13.8% | 56.4% | 62.0% |OK |
|
||||
| farmbot_os | 22.0% | 27.6% | 45.3% |OK |
|
||||
| farmbot_telemetry | ??.?% | ??.?% | ??.?% |LATER |
|
|
@ -21,10 +21,10 @@ string.
|
|||
cd $FARMBOT_OS_ROOT_DIRECTORY
|
||||
git checkout staging
|
||||
git fetch --all && git reset --hard origin/staging
|
||||
echo 10.5.6-rc30 > VERSION
|
||||
# update the CHANGELOG, but DO NOT put the `rc`
|
||||
# on the semver string.
|
||||
$EDITOR CHANGELOG.md
|
||||
echo 10.5.6-rc30 > VERSION
|
||||
git add CHANGELOG.md VERSION
|
||||
git commit -m "Release v10.5.6-rc30"
|
||||
git tag v$(cat VERSION)
|
||||
|
@ -84,8 +84,6 @@ done by pushing anything to the master branch:
|
|||
echo $NEW_VERSION > VERSION
|
||||
# update CHANGELOG.md
|
||||
$EDITOR CHANGELOG.md
|
||||
# update the download link in the readme
|
||||
$EDITOR README.md
|
||||
git checkout -b rel-$(cat VERSION)
|
||||
git commit -am 'Release v$(cat VERSION)'
|
||||
git push origin rel-$(cat VERSION)
|
||||
|
|
|
@ -189,7 +189,6 @@ defmodule FarmbotCore.Asset.Command do
|
|||
|
||||
# Catch-all use case:
|
||||
def update(asset_kind, id, params) do
|
||||
Logger.warn("AssetCommand needs implementation: #{asset_kind}")
|
||||
mod = as_module!(asset_kind)
|
||||
|
||||
case Repo.get_by(mod, id: id) do
|
||||
|
|
|
@ -94,7 +94,6 @@ defmodule FarmbotCore.AssetMonitor do
|
|||
Map.put(sub_state, id, updated_at)
|
||||
|
||||
is_nil(sub_state[id]) ->
|
||||
Logger.debug("#{inspect(kind)} #{id} needs to be started")
|
||||
asset = Repo.preload(asset, AssetWorker.preload(asset))
|
||||
:ok = AssetSupervisor.start_child(asset) |> assert_result!(asset)
|
||||
Map.put(sub_state, id, updated_at)
|
||||
|
|
|
@ -13,7 +13,8 @@ defmodule FarmbotCore.Leds.StubHandler do
|
|||
def white5(status), do: do_debug(:white, status)
|
||||
|
||||
defp do_debug(color, status) do
|
||||
msg = [IO.ANSI.reset(), "LED STATUS: ",
|
||||
unless System.get_env("LOG_SILENCE") do
|
||||
msg = [IO.ANSI.reset(), "LED STATUS: ",
|
||||
apply(IO.ANSI, color, []),
|
||||
status_in(status),
|
||||
to_string(color),
|
||||
|
@ -22,7 +23,8 @@ defmodule FarmbotCore.Leds.StubHandler do
|
|||
status_out(status),
|
||||
IO.ANSI.reset()
|
||||
]
|
||||
IO.puts(msg)
|
||||
IO.puts(msg)
|
||||
end
|
||||
end
|
||||
|
||||
defp status_in(:slow_blink), do: IO.ANSI.blink_slow()
|
||||
|
|
|
@ -23,7 +23,9 @@ defmodule FarmbotCore.LogExecutor do
|
|||
do: level,
|
||||
else: :info
|
||||
|
||||
Elixir.Logger.bare_log(logger_level, log, logger_meta)
|
||||
unless System.get_env("LOG_SILENCE") do
|
||||
Elixir.Logger.bare_log(logger_level, log, logger_meta)
|
||||
end
|
||||
log
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,3 +23,4 @@ erl_crash.dump
|
|||
farmbot_ext-*.tar
|
||||
|
||||
*.sqlite3
|
||||
*.coverdata
|
||||
|
|
|
@ -11,3 +11,4 @@ config :farmbot_celery_script, FarmbotCeleryScript.SysCalls,
|
|||
import_config "ecto.exs"
|
||||
import_config "farmbot_core.exs"
|
||||
import_config "lagger.exs"
|
||||
import_config "test.exs"
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
use Mix.Config
|
||||
|
||||
if Mix.env() == :test do
|
||||
config :farmbot_ext, FarmbotExt, children: []
|
||||
end
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"coverage_options": {
|
||||
"treat_no_relevant_lines_as_covered": true,
|
||||
"minimum_coverage": 6.5
|
||||
}
|
||||
}
|
|
@ -1,19 +1,18 @@
|
|||
defmodule FarmbotExt do
|
||||
# See https://hexdocs.pm/elixir/Application.html
|
||||
# for more information on OTP Applications
|
||||
@moduledoc false
|
||||
|
||||
use Application
|
||||
|
||||
def start(_type, _args) do
|
||||
# List all child processes to be supervised
|
||||
children = [
|
||||
FarmbotExt.Bootstrap
|
||||
]
|
||||
|
||||
# See https://hexdocs.pm/elixir/Supervisor.html
|
||||
# for other strategies and supported options
|
||||
opts = [strategy: :one_for_one, name: __MODULE__]
|
||||
Supervisor.start_link(children, opts)
|
||||
Supervisor.start_link(children(), opts)
|
||||
end
|
||||
|
||||
# This only exists because I was getting too many crashed
|
||||
# supervisor reports in the test suite (distraction from
|
||||
# real test failures).
|
||||
def children do
|
||||
config = Application.get_env(:farmbot_ext, __MODULE__) || []
|
||||
Keyword.get(config, :children, [FarmbotExt.Bootstrap])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
defmodule FarmbotExt.AMQP.AutoSyncAssetHandler do
|
||||
require Logger
|
||||
|
||||
alias FarmbotCore.{Asset, BotState, Leds}
|
||||
alias FarmbotExt.API.{EagerLoader}
|
||||
|
||||
# Sync messgages about these assets
|
||||
# should not be cached. They need to be applied
|
||||
# in real time.
|
||||
@no_cache_kinds ~w(
|
||||
Device
|
||||
FbosConfig
|
||||
FirmwareConfig
|
||||
FarmwareEnv
|
||||
FarmwareInstallation
|
||||
)
|
||||
|
||||
def handle_asset(asset_kind, id, params) do
|
||||
if Asset.Query.auto_sync?() do
|
||||
:ok = BotState.set_sync_status("syncing")
|
||||
_ = Leds.green(:really_fast_blink)
|
||||
Asset.Command.update(asset_kind, id, params)
|
||||
:ok = BotState.set_sync_status("synced")
|
||||
_ = Leds.green(:solid)
|
||||
else
|
||||
cache_sync(asset_kind, id, params)
|
||||
end
|
||||
end
|
||||
|
||||
def cache_sync(kind, id, params) when kind in @no_cache_kinds do
|
||||
:ok = Asset.Command.update(kind, id, params)
|
||||
end
|
||||
|
||||
def cache_sync(_, _, nil) do
|
||||
:ok = BotState.set_sync_status("sync_now")
|
||||
_ = Leds.green(:slow_blink)
|
||||
end
|
||||
|
||||
def cache_sync(asset_kind, id, params) do
|
||||
Logger.info("Autocaching sync #{asset_kind} #{id} #{inspect(params)}")
|
||||
changeset = Asset.Command.new_changeset(asset_kind, id, params)
|
||||
:ok = EagerLoader.cache(changeset)
|
||||
:ok = BotState.set_sync_status("sync_now")
|
||||
_ = Leds.green(:slow_blink)
|
||||
end
|
||||
end
|
|
@ -8,14 +8,13 @@ defmodule FarmbotExt.AMQP.AutoSyncChannel do
|
|||
use GenServer
|
||||
use AMQP
|
||||
|
||||
alias FarmbotCore.{Asset, BotState, JSON, Leds}
|
||||
alias FarmbotExt.AMQP.ConnectionWorker
|
||||
alias FarmbotExt.API.{EagerLoader, Preloader}
|
||||
|
||||
require Logger
|
||||
require FarmbotCore.Logger
|
||||
require FarmbotTelemetry
|
||||
|
||||
alias FarmbotCore.{Asset, BotState, JSON, Leds}
|
||||
alias FarmbotExt.AMQP.{ConnectionWorker, AutoSyncAssetHandler}
|
||||
alias FarmbotExt.API.{EagerLoader, Preloader}
|
||||
|
||||
# The API dispatches messages for other resources, but these
|
||||
# are the only ones that Farmbot needs to sync.
|
||||
@known_kinds ~w(
|
||||
|
@ -35,18 +34,8 @@ defmodule FarmbotExt.AMQP.AutoSyncChannel do
|
|||
Tool
|
||||
)
|
||||
|
||||
# Sync messgaes about these assets
|
||||
# should not be cached. They need to be applied
|
||||
# in real time.
|
||||
@no_cache_kinds ~w(
|
||||
Device
|
||||
FbosConfig
|
||||
FirmwareConfig
|
||||
FarmwareEnv
|
||||
FarmwareInstallation
|
||||
)
|
||||
|
||||
defstruct [:conn, :chan, :jwt, :preloaded]
|
||||
|
||||
alias __MODULE__, as: State
|
||||
|
||||
@doc "Gets status of auto_sync connection for diagnostics / tests."
|
||||
|
@ -68,12 +57,15 @@ defmodule FarmbotExt.AMQP.AutoSyncChannel do
|
|||
def terminate(reason, state) do
|
||||
FarmbotCore.Logger.error(1, "Disconnected from AutoSync channel: #{inspect(reason)}")
|
||||
# If a channel was still open, close it.
|
||||
if state.chan, do: ConnectionWorker.close_channel(state.chan)
|
||||
if state.chan do
|
||||
ConnectionWorker.close_channel(state.chan)
|
||||
end
|
||||
|
||||
try do
|
||||
EagerLoader.Supervisor.drop_all_cache()
|
||||
catch
|
||||
_, _ -> :ok
|
||||
_, _ ->
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -103,6 +95,7 @@ defmodule FarmbotExt.AMQP.AutoSyncChannel do
|
|||
end
|
||||
|
||||
def handle_info(:connect, state) do
|
||||
# THIS IS WHERE state.chan GETS SET
|
||||
result = ConnectionWorker.maybe_connect_autosync(state.jwt.bot)
|
||||
compute_reply_from_amqp_state(state, result)
|
||||
end
|
||||
|
@ -134,13 +127,11 @@ defmodule FarmbotExt.AMQP.AutoSyncChannel do
|
|||
case String.split(key, ".") do
|
||||
["bot", ^device, "sync", asset_kind, id_str] when asset_kind in @known_kinds ->
|
||||
id = data["id"] || String.to_integer(id_str)
|
||||
_ = handle_asset(asset_kind, id, body)
|
||||
|
||||
["bot", ^device, "sync", asset_kind, _id_str] ->
|
||||
Logger.warn("Unknown syncable asset: #{asset_kind}")
|
||||
_ = AutoSyncAssetHandler.handle_asset(asset_kind, id, body)
|
||||
|
||||
_ ->
|
||||
Logger.info("ignoring route: #{key}")
|
||||
""
|
||||
# Logger.info("ignoring route: #{key}")
|
||||
end
|
||||
|
||||
:ok = ConnectionWorker.rpc_reply(chan, device, label)
|
||||
|
@ -158,36 +149,6 @@ defmodule FarmbotExt.AMQP.AutoSyncChannel do
|
|||
{:reply, reply, state}
|
||||
end
|
||||
|
||||
def handle_asset(asset_kind, id, params) do
|
||||
if Asset.Query.auto_sync?() do
|
||||
:ok = BotState.set_sync_status("syncing")
|
||||
_ = Leds.green(:really_fast_blink)
|
||||
# Logger.info "Syncing #{asset_kind} #{id} #{inspect(params)}"
|
||||
Asset.Command.update(asset_kind, id, params)
|
||||
:ok = BotState.set_sync_status("synced")
|
||||
_ = Leds.green(:solid)
|
||||
else
|
||||
cache_sync(asset_kind, id, params)
|
||||
end
|
||||
end
|
||||
|
||||
def cache_sync(kind, id, params) when kind in @no_cache_kinds do
|
||||
:ok = Asset.Command.update(kind, id, params)
|
||||
end
|
||||
|
||||
def cache_sync(_, _, nil) do
|
||||
:ok = BotState.set_sync_status("sync_now")
|
||||
_ = Leds.green(:slow_blink)
|
||||
end
|
||||
|
||||
def cache_sync(asset_kind, id, params) do
|
||||
Logger.info("Autocaching sync #{asset_kind} #{id} #{inspect(params)}")
|
||||
changeset = Asset.Command.new_changeset(asset_kind, id, params)
|
||||
:ok = EagerLoader.cache(changeset)
|
||||
:ok = BotState.set_sync_status("sync_now")
|
||||
_ = Leds.green(:slow_blink)
|
||||
end
|
||||
|
||||
defp compute_reply_from_amqp_state(state, %{conn: conn, chan: chan}) do
|
||||
{:noreply, %{state | conn: conn, chan: chan}}
|
||||
end
|
||||
|
|
|
@ -66,7 +66,6 @@ defmodule FarmbotExt.API.EagerLoader do
|
|||
* a remote `id` field.
|
||||
"""
|
||||
def cache(%Changeset{data: %module{}} = changeset) do
|
||||
Logger.info("Caching #{inspect(changeset)}")
|
||||
id = Changeset.get_field(changeset, :id)
|
||||
updated_at = Changeset.get_field(changeset, :updated_at)
|
||||
id || change_error(changeset, "Can't cache a changeset with no :id attribute")
|
||||
|
|
|
@ -24,7 +24,7 @@ defmodule FarmbotExt.API.Preloader do
|
|||
with {:ok, sync_changeset} <- API.get_changeset(Sync),
|
||||
sync_changeset <- Reconciler.sync_group(sync_changeset, SyncGroup.group_0()) do
|
||||
FarmbotCore.Logger.success(3, "Successfully preloaded resources.")
|
||||
maybe_auto_sync(sync_changeset, Query.auto_sync?())
|
||||
maybe_auto_sync(sync_changeset, Query.auto_sync?() || false)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -51,8 +51,6 @@ defmodule FarmbotExt.Bootstrap do
|
|||
end
|
||||
|
||||
def try_auth(email, server, password, _secret) do
|
||||
Logger.debug("using password to auth")
|
||||
|
||||
with {:ok, tkn} <- Authorization.authorize_with_password(email, password, server),
|
||||
_ <- update_config_value(:string, "authorization", "token", tkn),
|
||||
{:ok, pid} <- Supervisor.start_child(FarmbotExt, Bootstrap.Supervisor) do
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
defmodule FarmbotExt.Bootstrap.Supervisor do
|
||||
@moduledoc """
|
||||
Supervisor responsible for starting all
|
||||
Supervisor responsible for starting all
|
||||
the tasks and processes that require authentication.
|
||||
"""
|
||||
use Supervisor
|
||||
|
@ -20,7 +20,6 @@ defmodule FarmbotExt.Bootstrap.Supervisor do
|
|||
FarmbotExt.Bootstrap.DropPasswordTask
|
||||
]
|
||||
|
||||
opts = [strategy: :one_for_one]
|
||||
Supervisor.init(children, opts)
|
||||
Supervisor.init(children, strategy: :one_for_one)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
defmodule AutoSyncChannelTest do
|
||||
require Helpers
|
||||
use ExUnit.Case, async: true
|
||||
use Mimic
|
||||
alias FarmbotExt.AMQP.AutoSyncChannel
|
||||
|
||||
use ExUnit.Case
|
||||
use Mimic
|
||||
|
||||
alias FarmbotCore.JSON
|
||||
|
||||
alias FarmbotCore.Asset.{
|
||||
Query,
|
||||
Command,
|
||||
Sync
|
||||
alias FarmbotExt.{
|
||||
AMQP.ConnectionWorker,
|
||||
API.Preloader,
|
||||
JWT
|
||||
}
|
||||
|
||||
alias FarmbotExt.{JWT, API, AMQP.ConnectionWorker}
|
||||
setup :verify_on_exit!
|
||||
setup :set_mimic_global
|
||||
|
||||
|
@ -35,203 +32,127 @@ defmodule AutoSyncChannelTest do
|
|||
"eXTEVkqw7rved84ogw6EKBSFCVqwRA-NKWLpPMV_q7fRwiEG" <>
|
||||
"Wj7R-KZqRweALXuvCLF765E6-ENxA"
|
||||
|
||||
def pretend_network_returned(fake_value) do
|
||||
def generate_pid do
|
||||
apply_default_mocks()
|
||||
jwt = JWT.decode!(@fake_jwt)
|
||||
|
||||
test_pid = self()
|
||||
|
||||
expect(Query, :auto_sync?, 2, fn -> false end)
|
||||
|
||||
expect(API, :get_changeset, fn _module ->
|
||||
send(test_pid, :preload_all_called)
|
||||
changeset = Sync.changeset(%Sync{}, %{})
|
||||
{:ok, changeset}
|
||||
end)
|
||||
|
||||
expect(ConnectionWorker, :maybe_connect_autosync, fn jwt ->
|
||||
send(test_pid, {:maybe_connect_called, jwt})
|
||||
fake_value
|
||||
end)
|
||||
|
||||
stub(ConnectionWorker, :close_channel, fn _ ->
|
||||
send(test_pid, :close_channel_called)
|
||||
:ok
|
||||
end)
|
||||
|
||||
stub(ConnectionWorker, :rpc_reply, fn chan, jwt_dot_bot, label ->
|
||||
send(test_pid, {:rpc_reply_called, chan, jwt_dot_bot, label})
|
||||
:ok
|
||||
end)
|
||||
|
||||
{:ok, pid} = AutoSyncChannel.start_link([jwt: jwt], [])
|
||||
|
||||
Map.merge(%{pid: pid}, AutoSyncChannel.network_status(pid))
|
||||
pid
|
||||
end
|
||||
|
||||
def under_normal_conditions() do
|
||||
fake_con = %{fake: :conn}
|
||||
fake_chan = %{fake: :chan}
|
||||
pretend_network_returned(%{conn: fake_con, chan: fake_chan})
|
||||
def apply_default_mocks do
|
||||
ok1 = fn _ -> :whatever end
|
||||
stub(FarmbotExt.API.EagerLoader.Supervisor, :drop_all_cache, fn -> :ok end)
|
||||
stub(ConnectionWorker, :close_channel, ok1)
|
||||
|
||||
stub(ConnectionWorker, :maybe_connect_autosync, fn _ ->
|
||||
%{conn: %{fake_conn: true}, chan: %{fake_chan: true}}
|
||||
end)
|
||||
end
|
||||
|
||||
test "network returns `nil`" do
|
||||
results = pretend_network_returned(nil)
|
||||
%{conn: has_conn, chan: has_chan, preloaded: is_preloaded} = results
|
||||
|
||||
assert has_chan == nil
|
||||
assert has_conn == nil
|
||||
assert is_preloaded
|
||||
def ensure_response_to(msg) do
|
||||
# Not much to check here other than matching clauses.
|
||||
# AMQP lib handles most all of this.
|
||||
expect(Preloader, :preload_all, 1, fn -> :ok end)
|
||||
pid = generate_pid()
|
||||
send(pid, msg)
|
||||
Process.sleep(5)
|
||||
end
|
||||
|
||||
test "network returns unexpected object (probably an error)" do
|
||||
results = pretend_network_returned({:something, :else})
|
||||
%{conn: has_conn, chan: has_chan, preloaded: is_preloaded} = results
|
||||
test "basic_cancel", do: ensure_response_to({:basic_cancel, :anything})
|
||||
test "basic_cancel_ok", do: ensure_response_to({:basic_cancel_ok, :anything})
|
||||
test "basic_consume_ok", do: ensure_response_to({:basic_consume_ok, :anything})
|
||||
|
||||
assert has_chan == nil
|
||||
assert has_conn == nil
|
||||
assert is_preloaded
|
||||
test "init / terminate - auto_sync enabled" do
|
||||
expect(Preloader, :preload_all, 1, fn -> :ok end)
|
||||
expect(FarmbotCore.Asset.Query, :auto_sync?, 1, fn -> true end)
|
||||
expect(FarmbotCore.BotState, :set_sync_status, 1, fn "synced" -> :ok end)
|
||||
|
||||
expect(FarmbotCore.Leds, :green, 2, fn
|
||||
:solid ->
|
||||
:ok
|
||||
|
||||
:really_fast_blink ->
|
||||
:ok
|
||||
end)
|
||||
|
||||
# Helpers.expect_log("Failed to connect to AutoSync channel: :whatever")
|
||||
# Helpers.expect_log("Disconnected from AutoSync channel: :normal")
|
||||
pid = generate_pid()
|
||||
assert %{chan: nil, conn: nil, preloaded: true} == AutoSyncChannel.network_status(pid)
|
||||
GenServer.stop(pid, :normal)
|
||||
end
|
||||
|
||||
test "catch-all clause for inbound AMQP messages" do
|
||||
fake_con = %{fake: :conn}
|
||||
fake_chan = %{fake: :chan}
|
||||
fake_response = %{conn: fake_con, chan: fake_chan}
|
||||
test "init / terminate - auto_sync disabled" do
|
||||
expect(Preloader, :preload_all, 1, fn -> :ok end)
|
||||
expect(FarmbotCore.Asset.Query, :auto_sync?, 1, fn -> false end)
|
||||
expect(FarmbotCore.BotState, :set_sync_status, 1, fn "sync_now" -> :ok end)
|
||||
|
||||
%{pid: pid} = pretend_network_returned(fake_response)
|
||||
expect(FarmbotCore.Leds, :green, 2, fn
|
||||
:slow_blink ->
|
||||
:ok
|
||||
|
||||
payload =
|
||||
JSON.encode!(%{
|
||||
args: %{label: "xyz"}
|
||||
:really_fast_blink ->
|
||||
:ok
|
||||
end)
|
||||
|
||||
Helpers.expect_log("Disconnected from AutoSync channel: :normal")
|
||||
pid = generate_pid()
|
||||
assert %{chan: nil, conn: nil, preloaded: true} == AutoSyncChannel.network_status(pid)
|
||||
GenServer.stop(pid, :normal)
|
||||
end
|
||||
|
||||
test "init / terminate - auto_sync error" do
|
||||
Helpers.expect_log("Error preloading. #{inspect("a test example")}")
|
||||
Helpers.expect_log("Disconnected from AutoSync channel: :normal")
|
||||
expect(FarmbotCore.BotState, :set_sync_status, 1, fn "sync_error" -> :ok end)
|
||||
expect(Preloader, :preload_all, 1, fn -> {:error, "a test example"} end)
|
||||
|
||||
expect(FarmbotCore.Leds, :green, 2, fn
|
||||
:slow_blink ->
|
||||
:ok
|
||||
|
||||
:really_fast_blink ->
|
||||
:ok
|
||||
end)
|
||||
|
||||
pid = generate_pid()
|
||||
assert %{chan: nil, conn: nil, preloaded: false} == AutoSyncChannel.network_status(pid)
|
||||
GenServer.stop(pid, :normal)
|
||||
end
|
||||
|
||||
test "delivery of auto sync messages" do
|
||||
expect(Preloader, :preload_all, 1, fn -> :ok end)
|
||||
|
||||
expect(ConnectionWorker, :rpc_reply, 1, fn chan, device, label ->
|
||||
assert chan == %{fake_chan: true}
|
||||
assert device == "device_15"
|
||||
assert label == "thisismylabelinatestsuite"
|
||||
:ok
|
||||
end)
|
||||
|
||||
key = "bot.device_15.sync.Device.46"
|
||||
|
||||
{:ok, payload} =
|
||||
FarmbotCore.JSON.encode(%{
|
||||
"id" => 46,
|
||||
"args" => %{
|
||||
"label" => "thisismylabelinatestsuite"
|
||||
},
|
||||
"body" => %{name: "This is my bot"}
|
||||
})
|
||||
|
||||
send(pid, {:basic_deliver, payload, %{routing_key: "WRONG!"}})
|
||||
assert_receive {:rpc_reply_called, %{fake: :chan}, "device_15", "xyz"}
|
||||
end
|
||||
|
||||
test "wont autosync unknown assets" do
|
||||
fake_con = %{fake: :conn}
|
||||
fake_chan = %{fake: :chan}
|
||||
fake_response = %{conn: fake_con, chan: fake_chan}
|
||||
|
||||
%{pid: pid} = pretend_network_returned(fake_response)
|
||||
|
||||
payload =
|
||||
JSON.encode!(%{
|
||||
args: %{label: "xyz"}
|
||||
})
|
||||
|
||||
send(pid, {:basic_deliver, payload, %{routing_key: "bot.device_15.sync.SavedGarden.999"}})
|
||||
assert_receive {:rpc_reply_called, %{fake: :chan}, "device_15", "xyz"}
|
||||
end
|
||||
|
||||
test "ignores asset deletion when auto_sync is off" do
|
||||
%{pid: pid} = under_normal_conditions()
|
||||
test_pid = self()
|
||||
payload = '{"args":{"label":"foo"}}'
|
||||
key = "bot.device_15.sync.Device.999"
|
||||
|
||||
stub(Query, :auto_sync?, fn ->
|
||||
send(test_pid, :called_auto_sync?)
|
||||
false
|
||||
end)
|
||||
|
||||
send(pid, {:basic_deliver, payload, %{routing_key: key}})
|
||||
assert_receive :called_auto_sync?
|
||||
end
|
||||
|
||||
test "handles Device assets" do
|
||||
%{pid: pid} = under_normal_conditions()
|
||||
test_pid = self()
|
||||
payload = '{"args":{"label":"foo"},"body":{}}'
|
||||
key = "bot.device_15.sync.Device.999"
|
||||
stub(Query, :auto_sync?, fn -> true end)
|
||||
|
||||
stub(Command, :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, "Device", 999, %{}}
|
||||
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(Query, :auto_sync?, fn -> true end)
|
||||
|
||||
stub(Command, :update, fn x, y, z ->
|
||||
send(test_pid, {:update_called, x, y, z})
|
||||
:ok
|
||||
end)
|
||||
|
||||
stub(Command, :update, fn x, y, z ->
|
||||
send(test_pid, {:update_called, x, y, z})
|
||||
:ok
|
||||
end)
|
||||
|
||||
pid = generate_pid()
|
||||
# We need the process to be preloaded for these tests to work:
|
||||
%{preloaded: true} = AutoSyncChannel.network_status(pid)
|
||||
send(pid, {:basic_deliver, payload, %{routing_key: key}})
|
||||
|
||||
assert_receive {:update_called, ^module_name, 999, %{"foo" => "bar"}}
|
||||
end
|
||||
|
||||
test "handles auto_sync of 'no_cache' 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(Query, :auto_sync?, fn ->
|
||||
send(test_pid, :called_auto_sync?)
|
||||
false
|
||||
end)
|
||||
|
||||
stub(Command, :update, fn kind, id, params ->
|
||||
send(test_pid, {:update_called, kind, id, params})
|
||||
expect(FarmbotExt.AMQP.AutoSyncAssetHandler, :handle_asset, fn kind, id, body ->
|
||||
assert kind == "Device"
|
||||
assert id == 46
|
||||
assert body == %{"name" => "This is my bot"}
|
||||
:ok
|
||||
end)
|
||||
|
||||
send(pid, {:basic_deliver, payload, %{routing_key: key}})
|
||||
assert_receive :called_auto_sync?
|
||||
assert_receive {:update_called, "FbosConfig", 999, %{"foo" => "bar"}}
|
||||
end
|
||||
|
||||
test "auto_sync disabled, resource not in @cache_kinds" do
|
||||
under_normal_conditions()
|
||||
|
||||
stub(Query, :auto_sync?, fn ->
|
||||
false
|
||||
end)
|
||||
|
||||
stub(Command, :new_changeset, fn _kind, _id, _params ->
|
||||
:ok
|
||||
end)
|
||||
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")
|
||||
|
||||
defp 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(Query, :auto_sync?, fn -> true end)
|
||||
|
||||
stub(Command, :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, 999, %{"foo" => "bar"}}
|
||||
Process.sleep(1000)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
defmodule FarmbotExt.AMQP.BotStateChannelTest do
|
||||
use ExUnit.Case
|
||||
use Mimic
|
||||
|
||||
# alias FarmbotExt.AMQP.BotStateChannel
|
||||
# alias FarmbotCore.BotState
|
||||
|
||||
setup :verify_on_exit!
|
||||
setup :set_mimic_global
|
||||
|
||||
defmodule FakeState do
|
||||
defstruct conn: %{fake: :conn}, chan: "fake_chan_", jwt: "fake_jwt_", cache: %{fake: :cache}
|
||||
end
|
||||
|
||||
test "terminate" do
|
||||
expected = "Disconnected from BotState channel: \"foo\""
|
||||
expect(AMQP.Channel, :close, 1, fn "fake_chan_" -> :ok end)
|
||||
|
||||
expect(FarmbotCore.LogExecutor, :execute, 1, fn log ->
|
||||
assert log.message == expected
|
||||
end)
|
||||
|
||||
FarmbotExt.AMQP.BotStateChannel.terminate("foo", %FakeState{})
|
||||
end
|
||||
end
|
|
@ -0,0 +1,12 @@
|
|||
defmodule FarmbotExt.API.ViewTest do
|
||||
use ExUnit.Case
|
||||
|
||||
def render(%{ok: :ok}) do
|
||||
:yep
|
||||
end
|
||||
|
||||
test "render/2" do
|
||||
result = FarmbotExt.API.View.render(__MODULE__, %{ok: :ok})
|
||||
assert :yep == result
|
||||
end
|
||||
end
|
|
@ -1,14 +1,33 @@
|
|||
Application.ensure_all_started(:farmbot)
|
||||
timeout = System.get_env("EXUNIT_TIMEOUT")
|
||||
|
||||
Mimic.copy(AMQP.Channel)
|
||||
Mimic.copy(FarmbotCeleryScript.SysCalls.Stubs)
|
||||
Mimic.copy(FarmbotCore.Asset.Command)
|
||||
Mimic.copy(FarmbotCore.Asset.Query)
|
||||
Mimic.copy(FarmbotCore.BotState)
|
||||
Mimic.copy(FarmbotCore.Leds)
|
||||
Mimic.copy(FarmbotCore.LogExecutor)
|
||||
Mimic.copy(FarmbotExt.AMQP.ConnectionWorker)
|
||||
Mimic.copy(FarmbotExt.API.EagerLoader.Supervisor)
|
||||
Mimic.copy(FarmbotExt.API.Preloader)
|
||||
Mimic.copy(FarmbotExt.API)
|
||||
Mimic.copy(FarmbotExt.AMQP.AutoSyncAssetHandler)
|
||||
|
||||
timeout = System.get_env("EXUNIT_TIMEOUT")
|
||||
System.put_env("LOG_SILENCE", "true")
|
||||
|
||||
if timeout do
|
||||
ExUnit.start(assert_receive_timeout: String.to_integer(timeout))
|
||||
else
|
||||
ExUnit.start()
|
||||
end
|
||||
|
||||
defmodule Helpers do
|
||||
defmacro expect_log(message) do
|
||||
quote do
|
||||
expect(FarmbotCore.LogExecutor, :execute, fn log ->
|
||||
assert log.message == unquote(message)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -300,20 +300,10 @@ defmodule FarmbotFirmware do
|
|||
name: state.reset
|
||||
) do
|
||||
{:ok, pid} ->
|
||||
Logger.debug(
|
||||
"Firmware reset #{state.reset} started. #{
|
||||
inspect(state.transport_args)
|
||||
}"
|
||||
)
|
||||
|
||||
{:noreply, %{state | reset_pid: pid}}
|
||||
|
||||
# TODO(Rick): I have no idea what's going on here.
|
||||
{:error, {:already_started, pid}} ->
|
||||
Logger.debug(
|
||||
"Firmware reset complete. #{inspect(state.transport_args)}"
|
||||
)
|
||||
|
||||
{:noreply, %{state | reset_pid: pid}}
|
||||
|
||||
error ->
|
||||
|
|
Loading…
Reference in New Issue