From 128ed8ec43ca2aa5a86d566b8373ad3c6b204f5e Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Thu, 26 Mar 2020 16:30:02 -0500 Subject: [PATCH 01/14] No supervisor children when in test env for FarmbotExt.Bootstrap.Supervisor --- farmbot_ext/config/test.exs | 1 + .../lib/farmbot_ext/api/image_uploader.ex | 2 +- .../lib/farmbot_ext/bootstrap/supervisor.ex | 11 ++++--- .../farmbot_ext/api/image_uploader_test.exs | 26 +++++++++++++++ farmbot_ext/test/test_helper.exs | 33 +++++++++++++++++++ 5 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 farmbot_ext/test/farmbot_ext/api/image_uploader_test.exs diff --git a/farmbot_ext/config/test.exs b/farmbot_ext/config/test.exs index 84f43b1f..e50f2aaa 100644 --- a/farmbot_ext/config/test.exs +++ b/farmbot_ext/config/test.exs @@ -2,4 +2,5 @@ use Mix.Config if Mix.env() == :test do config :farmbot_ext, FarmbotExt, children: [] + config :farmbot_ext, FarmbotExt.Bootstrap.Supervisor, children: [] end diff --git a/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex b/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex index 9a4af358..676b1d4f 100644 --- a/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex +++ b/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex @@ -16,7 +16,7 @@ defmodule FarmbotExt.API.ImageUploader do GenServer.start_link(__MODULE__, args, name: __MODULE__) end - def force_checkup do + def force_checkup() do GenServer.cast(__MODULE__, :force_checkup) end diff --git a/farmbot_ext/lib/farmbot_ext/bootstrap/supervisor.ex b/farmbot_ext/lib/farmbot_ext/bootstrap/supervisor.ex index 5abcdfb1..51d933d2 100644 --- a/farmbot_ext/lib/farmbot_ext/bootstrap/supervisor.ex +++ b/farmbot_ext/lib/farmbot_ext/bootstrap/supervisor.ex @@ -12,14 +12,17 @@ defmodule FarmbotExt.Bootstrap.Supervisor do @impl Supervisor def init([]) do - children = [ + Supervisor.init(children(), strategy: :one_for_one) + end + + def children() do + config = Application.get_env(:farmbot_ext, __MODULE__) || [] + Keyword.get(config, :children, [ FarmbotExt.API.EagerLoader.Supervisor, FarmbotExt.API.DirtyWorker.Supervisor, FarmbotExt.AMQP.Supervisor, FarmbotExt.API.ImageUploader, FarmbotExt.Bootstrap.DropPasswordTask - ] - - Supervisor.init(children, strategy: :one_for_one) + ]) end end diff --git a/farmbot_ext/test/farmbot_ext/api/image_uploader_test.exs b/farmbot_ext/test/farmbot_ext/api/image_uploader_test.exs new file mode 100644 index 00000000..043329a2 --- /dev/null +++ b/farmbot_ext/test/farmbot_ext/api/image_uploader_test.exs @@ -0,0 +1,26 @@ +defmodule FarmbotExt.API.ImageUploaderTest do + use ExUnit.Case + use Mimic + alias FarmbotExt.API.ImageUploader + setup :verify_on_exit! + + test "force checkup" do + Helpers.NamedProcess.start_link({ImageUploader, "force_checkup_test"}) + # TODO: Get some single pixel jpg, jpeg, png, gif files. + # TODO: Stub `API.upload_image` + + # upload_image_mock = fn _fname -> + # raise "HMMM...." + # end + + mapper = fn fname -> + File.touch!("/tmp/images/#{fname}") + end + + # expect(FarmbotExt.API, :upload_image, 3, upload_image_mock) + ["a.jpg", "b.jpeg", "c.png", "d.gif"] |> Enum.map(mapper) + ImageUploader.force_checkup() + Process.sleep(100) + assert_receive :lol + end +end diff --git a/farmbot_ext/test/test_helper.exs b/farmbot_ext/test/test_helper.exs index 9fbbf006..e76f3909 100644 --- a/farmbot_ext/test/test_helper.exs +++ b/farmbot_ext/test/test_helper.exs @@ -31,3 +31,36 @@ defmodule Helpers do end end end + +defmodule Helpers.NamedProcess do + @moduledoc """ + NOTE: Inspired by ex_venture test suite. Thanks! -RC + + Register a globaly named process that fakes out a normally real process. + + Any messages this process receives will forward them to the test process via `send`. + """ + + use GenServer + + @doc """ + Link a new process to the test process + + This takes place outside of the supervision tree, so the process does + not hang around. + """ + def start_link(name) do + GenServer.start_link(__MODULE__, [caller: self(), name: name], [name: {:global, name}]) + end + + @impl true + def init(state) do + {:ok, Enum.into(state, %{})} + end + + @impl true + def handle_cast(message, state) do + send(state.caller, {state.name, {:cast, message}}) + {:noreply, state} + end +end From 9a5b9279537b2d6261f8d659eba53784ee8ffefd Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Thu, 26 Mar 2020 17:06:55 -0500 Subject: [PATCH 02/14] FarmbotExt: Dont supervise anything in :test --- farmbot_ext/config/test.exs | 11 +++++-- .../farmbot_ext/amqp/channel_supervisor.ex | 12 ++++--- .../lib/farmbot_ext/amqp/supervisor.ex | 11 ++++--- .../api/dirty_worker/supervisor.ex | 12 ++++--- .../api/eager_loader/supervisor.ex | 12 ++++--- .../lib/farmbot_ext/bootstrap/supervisor.ex | 1 + farmbot_ext/test/test_helper.exs | 32 ------------------- 7 files changed, 40 insertions(+), 51 deletions(-) diff --git a/farmbot_ext/config/test.exs b/farmbot_ext/config/test.exs index e50f2aaa..6008aba9 100644 --- a/farmbot_ext/config/test.exs +++ b/farmbot_ext/config/test.exs @@ -1,6 +1,13 @@ use Mix.Config if Mix.env() == :test do - config :farmbot_ext, FarmbotExt, children: [] - config :farmbot_ext, FarmbotExt.Bootstrap.Supervisor, children: [] + mapper = fn mod -> config :farmbot_ext, mod, children: [] end + list = [ + FarmbotExt, + FarmbotExt.AMQP.ChannelSupervisor, + FarmbotExt.API.DirtyWorker.Supervisor, + FarmbotExt.API.EagerLoader.Supervisor, + FarmbotExt.Bootstrap.Supervisor, + ] + Enum.map(list, mapper) end diff --git a/farmbot_ext/lib/farmbot_ext/amqp/channel_supervisor.ex b/farmbot_ext/lib/farmbot_ext/amqp/channel_supervisor.ex index 69ad9992..00750952 100644 --- a/farmbot_ext/lib/farmbot_ext/amqp/channel_supervisor.ex +++ b/farmbot_ext/lib/farmbot_ext/amqp/channel_supervisor.ex @@ -19,17 +19,19 @@ defmodule FarmbotExt.AMQP.ChannelSupervisor do end def init([token]) do - jwt = JWT.decode!(token) + Supervisor.init(children(JWT.decode!(token)), strategy: :one_for_one) + end - children = [ + def children(jwt) do + config = Application.get_env(:farmbot_ext, __MODULE__) || [] + + Keyword.get(config, :children, [ {TelemetryChannel, [jwt: jwt]}, {LogChannel, [jwt: jwt]}, {PingPongChannel, [jwt: jwt]}, {BotStateChannel, [jwt: jwt]}, {AutoSyncChannel, [jwt: jwt]}, {CeleryScriptChannel, [jwt: jwt]} - ] - - Supervisor.init(children, strategy: :one_for_one) + ]) end end diff --git a/farmbot_ext/lib/farmbot_ext/amqp/supervisor.ex b/farmbot_ext/lib/farmbot_ext/amqp/supervisor.ex index edb58508..4147e1f2 100644 --- a/farmbot_ext/lib/farmbot_ext/amqp/supervisor.ex +++ b/farmbot_ext/lib/farmbot_ext/amqp/supervisor.ex @@ -10,14 +10,17 @@ defmodule FarmbotExt.AMQP.Supervisor do end def init([]) do + Supervisor.init(children(), strategy: :one_for_all) + end + + def children do token = get_config_value(:string, "authorization", "token") email = get_config_value(:string, "authorization", "email") + config = Application.get_env(:farmbot_ext, __MODULE__) || [] - children = [ + Keyword.get(config, :children, [ {FarmbotExt.AMQP.ConnectionWorker, [token: token, email: email]}, {FarmbotExt.AMQP.ChannelSupervisor, [token]} - ] - - Supervisor.init(children, strategy: :one_for_all) + ]) end end diff --git a/farmbot_ext/lib/farmbot_ext/api/dirty_worker/supervisor.ex b/farmbot_ext/lib/farmbot_ext/api/dirty_worker/supervisor.ex index b1e78da0..a026ea06 100644 --- a/farmbot_ext/lib/farmbot_ext/api/dirty_worker/supervisor.ex +++ b/farmbot_ext/lib/farmbot_ext/api/dirty_worker/supervisor.ex @@ -33,7 +33,13 @@ defmodule FarmbotExt.API.DirtyWorker.Supervisor do @impl Supervisor def init(_args) do - children = [ + Supervisor.init(children(), strategy: :one_for_one) + end + + def children do + config = Application.get_env(:farmbot_ext, __MODULE__) || [] + + Keyword.get(config, :children, [ {DirtyWorker, Device}, {DirtyWorker, DeviceCert}, {DirtyWorker, FbosConfig}, @@ -50,8 +56,6 @@ defmodule FarmbotExt.API.DirtyWorker.Supervisor do {DirtyWorker, Sensor}, {DirtyWorker, Sequence}, {DirtyWorker, Tool} - ] - - Supervisor.init(children, strategy: :one_for_one) + ]) end end diff --git a/farmbot_ext/lib/farmbot_ext/api/eager_loader/supervisor.ex b/farmbot_ext/lib/farmbot_ext/api/eager_loader/supervisor.ex index cf7dd358..086b77fa 100644 --- a/farmbot_ext/lib/farmbot_ext/api/eager_loader/supervisor.ex +++ b/farmbot_ext/lib/farmbot_ext/api/eager_loader/supervisor.ex @@ -39,7 +39,13 @@ defmodule FarmbotExt.API.EagerLoader.Supervisor do @impl Supervisor def init(_args) do - children = [ + Supervisor.init(children(), strategy: :one_for_one) + end + + def children do + config = Application.get_env(:farmbot_ext, __MODULE__) || [] + + Keyword.get(config, :children, [ {EagerLoader, Device}, {EagerLoader, FarmEvent}, {EagerLoader, FarmwareEnv}, @@ -56,8 +62,6 @@ defmodule FarmbotExt.API.EagerLoader.Supervisor do {EagerLoader, Sensor}, {EagerLoader, Sequence}, {EagerLoader, Tool} - ] - - Supervisor.init(children, strategy: :one_for_one) + ]) end end diff --git a/farmbot_ext/lib/farmbot_ext/bootstrap/supervisor.ex b/farmbot_ext/lib/farmbot_ext/bootstrap/supervisor.ex index 51d933d2..05ec931b 100644 --- a/farmbot_ext/lib/farmbot_ext/bootstrap/supervisor.ex +++ b/farmbot_ext/lib/farmbot_ext/bootstrap/supervisor.ex @@ -17,6 +17,7 @@ defmodule FarmbotExt.Bootstrap.Supervisor do def children() do config = Application.get_env(:farmbot_ext, __MODULE__) || [] + Keyword.get(config, :children, [ FarmbotExt.API.EagerLoader.Supervisor, FarmbotExt.API.DirtyWorker.Supervisor, diff --git a/farmbot_ext/test/test_helper.exs b/farmbot_ext/test/test_helper.exs index e76f3909..168c628b 100644 --- a/farmbot_ext/test/test_helper.exs +++ b/farmbot_ext/test/test_helper.exs @@ -32,35 +32,3 @@ defmodule Helpers do end end -defmodule Helpers.NamedProcess do - @moduledoc """ - NOTE: Inspired by ex_venture test suite. Thanks! -RC - - Register a globaly named process that fakes out a normally real process. - - Any messages this process receives will forward them to the test process via `send`. - """ - - use GenServer - - @doc """ - Link a new process to the test process - - This takes place outside of the supervision tree, so the process does - not hang around. - """ - def start_link(name) do - GenServer.start_link(__MODULE__, [caller: self(), name: name], [name: {:global, name}]) - end - - @impl true - def init(state) do - {:ok, Enum.into(state, %{})} - end - - @impl true - def handle_cast(message, state) do - send(state.caller, {state.name, {:cast, message}}) - {:noreply, state} - end -end From 33b0947c7ab72b2bf70275a8999409de66f0dfa1 Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Mon, 30 Mar 2020 17:43:29 -0500 Subject: [PATCH 03/14] Set `async` to `false` in a few missing places --- farmbot_ext/config/test.exs | 4 +- farmbot_ext/lib/farmbot_ext.ex | 8 ++-- .../lib/farmbot_ext/api/image_uploader.ex | 16 +++++--- .../amqp/auto_sync_asset_handler_test.exs | 2 +- .../amqp/auto_sync_channel_test.exs | 2 +- .../amqp/bot_state_channel_test.exs | 2 +- .../farmbot_ext/api/image_uploader_test.exs | 41 ++++++++++++------- farmbot_ext/test/test_helper.exs | 9 +--- 8 files changed, 49 insertions(+), 35 deletions(-) diff --git a/farmbot_ext/config/test.exs b/farmbot_ext/config/test.exs index 6008aba9..a6cd3dfe 100644 --- a/farmbot_ext/config/test.exs +++ b/farmbot_ext/config/test.exs @@ -2,12 +2,14 @@ use Mix.Config if Mix.env() == :test do mapper = fn mod -> config :farmbot_ext, mod, children: [] end + list = [ FarmbotExt, FarmbotExt.AMQP.ChannelSupervisor, FarmbotExt.API.DirtyWorker.Supervisor, FarmbotExt.API.EagerLoader.Supervisor, - FarmbotExt.Bootstrap.Supervisor, + FarmbotExt.Bootstrap.Supervisor ] + Enum.map(list, mapper) end diff --git a/farmbot_ext/lib/farmbot_ext.ex b/farmbot_ext/lib/farmbot_ext.ex index 51c37cf4..08317b1d 100644 --- a/farmbot_ext/lib/farmbot_ext.ex +++ b/farmbot_ext/lib/farmbot_ext.ex @@ -4,13 +4,11 @@ defmodule FarmbotExt do use Application def start(_type, _args) do - 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 opts, do: [strategy: :one_for_one, name: __MODULE__] + def children do config = Application.get_env(:farmbot_ext, __MODULE__) || [] Keyword.get(config, :children, [FarmbotExt.Bootstrap]) diff --git a/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex b/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex index 676b1d4f..f51e68cb 100644 --- a/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex +++ b/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex @@ -44,6 +44,8 @@ defmodule FarmbotExt.API.ImageUploader do end def handle_continue([], state), do: {:noreply, state, @checkup_time_ms} + # WIP - RC + def handle_call(:noop, _, s), do: {:reply, :ok, s} # the meta here is likely inaccurate here because of # pulling the location data from the cache instead of from the firmware @@ -52,12 +54,16 @@ defmodule FarmbotExt.API.ImageUploader do defp try_upload(image_filename) do %{x: x, y: y, z: z} = BotState.fetch().location_data.position meta = %{x: x, y: y, z: z, name: Path.rootname(image_filename)} + finalize(image_filename, API.upload_image(image_filename, meta)) + end - with {:ok, %{status: s, body: _body}} when s > 199 and s < 300 <- - API.upload_image(image_filename, meta) do - FarmbotCore.Logger.success(3, "Uploaded image: #{image_filename}") - File.rm(image_filename) - end + defp finalize(file, {:ok, %{status: s, body: _}}) when s > 199 and s < 300 do + FarmbotCore.Logger.success(3, "Uploaded image: #{file}") + File.rm(file) + end + + defp finalize(fname, other) do + FarmbotCore.Logger.success(3, "Upload Error (#{fname}): #{inspect(other)}") end # Stolen from diff --git a/farmbot_ext/test/farmbot_ext/amqp/auto_sync_asset_handler_test.exs b/farmbot_ext/test/farmbot_ext/amqp/auto_sync_asset_handler_test.exs index 950f1aad..384a8cd5 100644 --- a/farmbot_ext/test/farmbot_ext/amqp/auto_sync_asset_handler_test.exs +++ b/farmbot_ext/test/farmbot_ext/amqp/auto_sync_asset_handler_test.exs @@ -1,5 +1,5 @@ defmodule AutoSyncAssetHandlerTest do - use ExUnit.Case, async: true + use ExUnit.Case, async: false use Mimic setup :verify_on_exit! diff --git a/farmbot_ext/test/farmbot_ext/amqp/auto_sync_channel_test.exs b/farmbot_ext/test/farmbot_ext/amqp/auto_sync_channel_test.exs index e10ac3b4..a9c17719 100644 --- a/farmbot_ext/test/farmbot_ext/amqp/auto_sync_channel_test.exs +++ b/farmbot_ext/test/farmbot_ext/amqp/auto_sync_channel_test.exs @@ -1,6 +1,6 @@ defmodule AutoSyncChannelTest do require Helpers - use ExUnit.Case, async: true + use ExUnit.Case, async: false use Mimic alias FarmbotExt.AMQP.AutoSyncChannel diff --git a/farmbot_ext/test/farmbot_ext/amqp/bot_state_channel_test.exs b/farmbot_ext/test/farmbot_ext/amqp/bot_state_channel_test.exs index d03cf239..f7d8e284 100644 --- a/farmbot_ext/test/farmbot_ext/amqp/bot_state_channel_test.exs +++ b/farmbot_ext/test/farmbot_ext/amqp/bot_state_channel_test.exs @@ -1,5 +1,5 @@ defmodule FarmbotExt.AMQP.BotStateChannelTest do - use ExUnit.Case + use ExUnit.Case, async: false use Mimic # alias FarmbotExt.AMQP.BotStateChannel diff --git a/farmbot_ext/test/farmbot_ext/api/image_uploader_test.exs b/farmbot_ext/test/farmbot_ext/api/image_uploader_test.exs index 043329a2..566c4279 100644 --- a/farmbot_ext/test/farmbot_ext/api/image_uploader_test.exs +++ b/farmbot_ext/test/farmbot_ext/api/image_uploader_test.exs @@ -1,26 +1,39 @@ defmodule FarmbotExt.API.ImageUploaderTest do - use ExUnit.Case + use ExUnit.Case, async: false use Mimic alias FarmbotExt.API.ImageUploader setup :verify_on_exit! + setup :set_mimic_global + + # TODO: Get some single pixel jpg, jpeg, png, gif files. + # TODO: Stub `API.upload_image` + + # upload_image_mock = fn _fname -> + # raise "HMMM...." + # end test "force checkup" do - Helpers.NamedProcess.start_link({ImageUploader, "force_checkup_test"}) - # TODO: Get some single pixel jpg, jpeg, png, gif files. - # TODO: Stub `API.upload_image` + pid = + if Process.whereis(ImageUploader) do + Process.whereis(ImageUploader) + else + {:ok, p} = ImageUploader.start_link([]) + p + end - # upload_image_mock = fn _fname -> - # raise "HMMM...." - # end + # ref = Process.monitor(pid) + Enum.map( + ["a.jpg", "b.jpeg", "c.png", "d.gif"], + fn fname -> File.touch!("/tmp/images/#{fname}") end + ) - mapper = fn fname -> - File.touch!("/tmp/images/#{fname}") - end + expect(FarmbotExt.API, :upload_image, 1, fn _image_filename, _meta -> + IO.puts("-=-=--==-=-=-=-=--=-=-==--=-=-=-==-") + {:ok, %{status: 201, body: %{}}} + end) - # expect(FarmbotExt.API, :upload_image, 3, upload_image_mock) - ["a.jpg", "b.jpeg", "c.png", "d.gif"] |> Enum.map(mapper) ImageUploader.force_checkup() - Process.sleep(100) - assert_receive :lol + GenServer.call(pid, :noop) + send(pid, :timeout) end end diff --git a/farmbot_ext/test/test_helper.exs b/farmbot_ext/test/test_helper.exs index 168c628b..a6112427 100644 --- a/farmbot_ext/test/test_helper.exs +++ b/farmbot_ext/test/test_helper.exs @@ -13,14 +13,10 @@ Mimic.copy(FarmbotExt.API.Preloader) Mimic.copy(FarmbotExt.API) Mimic.copy(FarmbotExt.AMQP.AutoSyncAssetHandler) -timeout = System.get_env("EXUNIT_TIMEOUT") +timeout = System.get_env("EXUNIT_TIMEOUT") || "5000" System.put_env("LOG_SILENCE", "true") -if timeout do - ExUnit.start(assert_receive_timeout: String.to_integer(timeout)) -else - ExUnit.start() -end +ExUnit.start(assert_receive_timeout: String.to_integer(timeout)) defmodule Helpers do defmacro expect_log(message) do @@ -31,4 +27,3 @@ defmodule Helpers do end end end - From fe7726262694a4c066853d5d87bcc19cdb482ae0 Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Mon, 30 Mar 2020 18:38:41 -0500 Subject: [PATCH 04/14] Coverage for FarmbotExt.API.ImageUploader --- .../lib/farmbot_ext/api/image_uploader.ex | 4 +-- .../farmbot_ext/api/image_uploader_test.exs | 26 +++++++------------ 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex b/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex index f51e68cb..3bbd188a 100644 --- a/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex +++ b/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex @@ -44,7 +44,7 @@ defmodule FarmbotExt.API.ImageUploader do end def handle_continue([], state), do: {:noreply, state, @checkup_time_ms} - # WIP - RC + # This only exists to flush handle_cast's. I think. -RC def handle_call(:noop, _, s), do: {:reply, :ok, s} # the meta here is likely inaccurate here because of @@ -63,7 +63,7 @@ defmodule FarmbotExt.API.ImageUploader do end defp finalize(fname, other) do - FarmbotCore.Logger.success(3, "Upload Error (#{fname}): #{inspect(other)}") + FarmbotCore.Logger.error(3, "Upload Error (#{fname}): #{inspect(other)}") end # Stolen from diff --git a/farmbot_ext/test/farmbot_ext/api/image_uploader_test.exs b/farmbot_ext/test/farmbot_ext/api/image_uploader_test.exs index 566c4279..a139d8e4 100644 --- a/farmbot_ext/test/farmbot_ext/api/image_uploader_test.exs +++ b/farmbot_ext/test/farmbot_ext/api/image_uploader_test.exs @@ -5,13 +5,6 @@ defmodule FarmbotExt.API.ImageUploaderTest do setup :verify_on_exit! setup :set_mimic_global - # TODO: Get some single pixel jpg, jpeg, png, gif files. - # TODO: Stub `API.upload_image` - - # upload_image_mock = fn _fname -> - # raise "HMMM...." - # end - test "force checkup" do pid = if Process.whereis(ImageUploader) do @@ -21,19 +14,20 @@ defmodule FarmbotExt.API.ImageUploaderTest do p end - # ref = Process.monitor(pid) - Enum.map( - ["a.jpg", "b.jpeg", "c.png", "d.gif"], - fn fname -> File.touch!("/tmp/images/#{fname}") end - ) + ["a.jpg", "b.jpeg", "c.png", "d.gif"] + |> Enum.map(fn fname -> + f = "/tmp/images/#{fname}" + File.touch!(f) + File.write(f, "X") + end) - expect(FarmbotExt.API, :upload_image, 1, fn _image_filename, _meta -> - IO.puts("-=-=--==-=-=-=-=--=-=-==--=-=-=-==-") - {:ok, %{status: 201, body: %{}}} + expect(FarmbotExt.API, :upload_image, 4, fn + "/tmp/images/d.gif", _meta -> {:ok, %{status: 401, body: %{}}} + _image_filename, _meta -> {:ok, %{status: 201, body: %{}}} end) ImageUploader.force_checkup() - GenServer.call(pid, :noop) send(pid, :timeout) + :ok = GenServer.call(pid, :noop) end end From 7fad9e72317f07e11a87ff55e6c7c75f288d56a6 Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Mon, 30 Mar 2020 18:45:04 -0500 Subject: [PATCH 05/14] Formatting --- farmbot_ext/lib/farmbot_ext/api/image_uploader.ex | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex b/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex index 3bbd188a..b77f7fe7 100644 --- a/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex +++ b/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex @@ -47,10 +47,10 @@ defmodule FarmbotExt.API.ImageUploader do # This only exists to flush handle_cast's. I think. -RC def handle_call(:noop, _, s), do: {:reply, :ok, s} - # the meta here is likely inaccurate here because of - # pulling the location data from the cache instead of from the firmware - # directly. It's close enough and getting data from the firmware directly - # would require more work than it is worth + # the meta here is likely inaccurate here because of pulling the location data + # from the cache instead of from the firmware directly. It's close enough and + # getting data from the firmware directly would require more work than it is + # worth defp try_upload(image_filename) do %{x: x, y: y, z: z} = BotState.fetch().location_data.position meta = %{x: x, y: y, z: z, name: Path.rootname(image_filename)} From 06323242c23142dae63b920f38028000c8f5187e Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Tue, 31 Mar 2020 10:36:48 -0500 Subject: [PATCH 06/14] Add Helpers.wait_for(pid). Remove :noop call --- .../lib/farmbot_ext/api/image_uploader.ex | 2 -- .../farmbot_ext/api/image_uploader_test.exs | 14 ++++++++-- farmbot_ext/test/test_helper.exs | 26 +++++++++++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex b/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex index b77f7fe7..2755adeb 100644 --- a/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex +++ b/farmbot_ext/lib/farmbot_ext/api/image_uploader.ex @@ -44,8 +44,6 @@ defmodule FarmbotExt.API.ImageUploader do end def handle_continue([], state), do: {:noreply, state, @checkup_time_ms} - # This only exists to flush handle_cast's. I think. -RC - def handle_call(:noop, _, s), do: {:reply, :ok, s} # the meta here is likely inaccurate here because of pulling the location data # from the cache instead of from the firmware directly. It's close enough and diff --git a/farmbot_ext/test/farmbot_ext/api/image_uploader_test.exs b/farmbot_ext/test/farmbot_ext/api/image_uploader_test.exs index a139d8e4..41c788b0 100644 --- a/farmbot_ext/test/farmbot_ext/api/image_uploader_test.exs +++ b/farmbot_ext/test/farmbot_ext/api/image_uploader_test.exs @@ -1,4 +1,5 @@ defmodule FarmbotExt.API.ImageUploaderTest do + require Helpers use ExUnit.Case, async: false use Mimic alias FarmbotExt.API.ImageUploader @@ -22,12 +23,21 @@ defmodule FarmbotExt.API.ImageUploaderTest do end) expect(FarmbotExt.API, :upload_image, 4, fn - "/tmp/images/d.gif", _meta -> {:ok, %{status: 401, body: %{}}} + "/tmp/images/d.gif", _meta -> {:error, %{status: 401, body: %{}}} _image_filename, _meta -> {:ok, %{status: 201, body: %{}}} end) + err_msg = + "Upload Error (/tmp/images/d.gif): " <> + "{:error, %{body: %{}, status: 401}}" + + Helpers.expect_log("Uploaded image: /tmp/images/a.jpg") + Helpers.expect_log("Uploaded image: /tmp/images/b.jpeg") + Helpers.expect_log("Uploaded image: /tmp/images/c.png") + Helpers.expect_log(err_msg) + ImageUploader.force_checkup() send(pid, :timeout) - :ok = GenServer.call(pid, :noop) + Helpers.wait_for(pid) end end diff --git a/farmbot_ext/test/test_helper.exs b/farmbot_ext/test/test_helper.exs index a6112427..c4a98d2c 100644 --- a/farmbot_ext/test/test_helper.exs +++ b/farmbot_ext/test/test_helper.exs @@ -19,6 +19,32 @@ System.put_env("LOG_SILENCE", "true") ExUnit.start(assert_receive_timeout: String.to_integer(timeout)) defmodule Helpers do + # Maybe I don't need this? + # Maybe I could use `start_supervised`? + # https://hexdocs.pm/ex_unit/ExUnit.Callbacks.html#start_supervised/2 + + # Base case: We have a pid + def wait_for(pid) when is_pid(pid), do: continue_waiting(pid) + # Failure case: We failed to find a pid for a module. + def wait_for(nil), do: raise("Attempted to wait on bad module/pid") + # Edge case: We have a module and need to try finding its pid. + def wait_for(mod), do: wait_for(Process.whereis(mod)) + + defp continue_waiting(pid) do + wait(pid, Process.info(pid, :message_queue_len)) + end + + defp wait(_pid, {:message_queue_len, 0}), do: :ok + + defp wait(pid, {:message_queue_len, n}) when n < 20 do + Process.sleep(100) + continue_waiting(pid) + end + + defp wait(pid, {:message_queue_len, n}) do + raise "No longer waiting on #{inspect(pid)} after #{n} attempts" + end + defmacro expect_log(message) do quote do expect(FarmbotCore.LogExecutor, :execute, fn log -> From aa6f3f4ad1b9b72dc58550a5bf46929628ee0956 Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Tue, 31 Mar 2020 10:58:40 -0500 Subject: [PATCH 07/14] Race condition fix (probably) --- farmbot_ext/test/test_helper.exs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/farmbot_ext/test/test_helper.exs b/farmbot_ext/test/test_helper.exs index c4a98d2c..f425c329 100644 --- a/farmbot_ext/test/test_helper.exs +++ b/farmbot_ext/test/test_helper.exs @@ -23,6 +23,7 @@ defmodule Helpers do # Maybe I could use `start_supervised`? # https://hexdocs.pm/ex_unit/ExUnit.Callbacks.html#start_supervised/2 + @wait_time 25 # Base case: We have a pid def wait_for(pid) when is_pid(pid), do: continue_waiting(pid) # Failure case: We failed to find a pid for a module. @@ -34,17 +35,13 @@ defmodule Helpers do wait(pid, Process.info(pid, :message_queue_len)) end - defp wait(_pid, {:message_queue_len, 0}), do: :ok + defp wait(_pid, {:message_queue_len, 0}), do: Process.sleep(@wait_time) - defp wait(pid, {:message_queue_len, n}) when n < 20 do - Process.sleep(100) + defp wait(pid, {:message_queue_len, _n}) do + Process.sleep(@wait_time) continue_waiting(pid) end - defp wait(pid, {:message_queue_len, n}) do - raise "No longer waiting on #{inspect(pid)} after #{n} attempts" - end - defmacro expect_log(message) do quote do expect(FarmbotCore.LogExecutor, :execute, fn log -> From e5a29361d225bd89b89e3964b42223e3d7764514 Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Tue, 31 Mar 2020 11:09:56 -0500 Subject: [PATCH 08/14] :clap: Race conditions arre gone from test suite --- farmbot_ext/test/test_helper.exs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/farmbot_ext/test/test_helper.exs b/farmbot_ext/test/test_helper.exs index f425c329..378b7f50 100644 --- a/farmbot_ext/test/test_helper.exs +++ b/farmbot_ext/test/test_helper.exs @@ -23,24 +23,24 @@ defmodule Helpers do # Maybe I could use `start_supervised`? # https://hexdocs.pm/ex_unit/ExUnit.Callbacks.html#start_supervised/2 - @wait_time 25 + @wait_time 13 # Base case: We have a pid - def wait_for(pid) when is_pid(pid), do: continue_waiting(pid) + def wait_for(pid) when is_pid(pid), do: check_on_mbox(pid) # Failure case: We failed to find a pid for a module. def wait_for(nil), do: raise("Attempted to wait on bad module/pid") # Edge case: We have a module and need to try finding its pid. def wait_for(mod), do: wait_for(Process.whereis(mod)) - defp continue_waiting(pid) do + # Enter recursive loop + defp check_on_mbox(pid) do + Process.sleep(@wait_time) wait(pid, Process.info(pid, :message_queue_len)) end - defp wait(_pid, {:message_queue_len, 0}), do: Process.sleep(@wait_time) - - defp wait(pid, {:message_queue_len, _n}) do - Process.sleep(@wait_time) - continue_waiting(pid) - end + # Exit recursive loop + defp wait(_, {:message_queue_len, 0}), do: Process.sleep(@wait_time) + # Continue recursive loop + defp wait(pid, {:message_queue_len, _n}), do: check_on_mbox(pid) defmacro expect_log(message) do quote do From 04c74b28f2ef3a031e606050d12522ffd537fe86 Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Tue, 31 Mar 2020 11:37:23 -0500 Subject: [PATCH 09/14] Replace Process.sleep() with Helpers.wait_for(pid) --- .../test/farmbot_ext/amqp/auto_sync_channel_test.exs | 4 ++-- farmbot_ext/test/test_helper.exs | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/farmbot_ext/test/farmbot_ext/amqp/auto_sync_channel_test.exs b/farmbot_ext/test/farmbot_ext/amqp/auto_sync_channel_test.exs index a9c17719..9edcb60e 100644 --- a/farmbot_ext/test/farmbot_ext/amqp/auto_sync_channel_test.exs +++ b/farmbot_ext/test/farmbot_ext/amqp/auto_sync_channel_test.exs @@ -55,7 +55,7 @@ defmodule AutoSyncChannelTest do expect(Preloader, :preload_all, 1, fn -> :ok end) pid = generate_pid() send(pid, msg) - Process.sleep(5) + Helpers.wait_for(pid) end test "basic_cancel", do: ensure_response_to({:basic_cancel, :anything}) @@ -153,6 +153,6 @@ defmodule AutoSyncChannelTest do :ok end) - Process.sleep(1200) + Helpers.wait_for(pid) end end diff --git a/farmbot_ext/test/test_helper.exs b/farmbot_ext/test/test_helper.exs index 378b7f50..9b5bff4c 100644 --- a/farmbot_ext/test/test_helper.exs +++ b/farmbot_ext/test/test_helper.exs @@ -23,7 +23,7 @@ defmodule Helpers do # Maybe I could use `start_supervised`? # https://hexdocs.pm/ex_unit/ExUnit.Callbacks.html#start_supervised/2 - @wait_time 13 + @wait_time 15 # Base case: We have a pid def wait_for(pid) when is_pid(pid), do: check_on_mbox(pid) # Failure case: We failed to find a pid for a module. @@ -37,8 +37,11 @@ defmodule Helpers do wait(pid, Process.info(pid, :message_queue_len)) end - # Exit recursive loop - defp wait(_, {:message_queue_len, 0}), do: Process.sleep(@wait_time) + # Exit recursive loop (mbox is clear) + defp wait(_, {:message_queue_len, 0}), do: Process.sleep(@wait_time * 3) + # Exit recursive loop (pid is dead) + defp wait(_, nil), do: Process.sleep(@wait_time * 3) + # Continue recursive loop defp wait(pid, {:message_queue_len, _n}), do: check_on_mbox(pid) From 431e05284af8d7e6feceb4059d92344c58d50a28 Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Tue, 31 Mar 2020 11:49:46 -0500 Subject: [PATCH 10/14] [farmbot_ext: 12.1%] AutoSyncAssetHandler test for when auto_sync?() is enabled --- .../farmbot_ext/amqp/auto_sync_asset_handler_test.exs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/farmbot_ext/test/farmbot_ext/amqp/auto_sync_asset_handler_test.exs b/farmbot_ext/test/farmbot_ext/amqp/auto_sync_asset_handler_test.exs index 384a8cd5..ac80ac79 100644 --- a/farmbot_ext/test/farmbot_ext/amqp/auto_sync_asset_handler_test.exs +++ b/farmbot_ext/test/farmbot_ext/amqp/auto_sync_asset_handler_test.exs @@ -9,6 +9,7 @@ defmodule AutoSyncAssetHandlerTest do alias FarmbotCore.{Asset, BotState, Leds} def auto_sync_off, do: expect(Asset.Query, :auto_sync?, fn -> false end) + def auto_sync_on, do: expect(Asset.Query, :auto_sync?, fn -> true end) def expect_sync_status_to_be(status), do: expect(BotState, :set_sync_status, fn ^status -> :ok end) @@ -22,4 +23,13 @@ defmodule AutoSyncAssetHandlerTest do expect_green_leds(:slow_blink) AutoSyncAssetHandler.handle_asset("Point", 23, nil) end + + test "handling of deleted assets when auto_sync is enabled" do + auto_sync_on() + expect_sync_status_to_be("syncing") + expect_sync_status_to_be("synced") + expect_green_leds(:really_fast_blink) + expect_green_leds(:solid) + AutoSyncAssetHandler.handle_asset("Point", 32, nil) + end end From 78e44eeb817969e8fbdb8d8474533d1403a55fbf Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Tue, 31 Mar 2020 11:53:18 -0500 Subject: [PATCH 11/14] Increase wait times to account for slowness of CI? --- farmbot_ext/test/test_helper.exs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/farmbot_ext/test/test_helper.exs b/farmbot_ext/test/test_helper.exs index 9b5bff4c..57554e4d 100644 --- a/farmbot_ext/test/test_helper.exs +++ b/farmbot_ext/test/test_helper.exs @@ -23,7 +23,7 @@ defmodule Helpers do # Maybe I could use `start_supervised`? # https://hexdocs.pm/ex_unit/ExUnit.Callbacks.html#start_supervised/2 - @wait_time 15 + @wait_time 30 # Base case: We have a pid def wait_for(pid) when is_pid(pid), do: check_on_mbox(pid) # Failure case: We failed to find a pid for a module. @@ -38,9 +38,9 @@ defmodule Helpers do end # Exit recursive loop (mbox is clear) - defp wait(_, {:message_queue_len, 0}), do: Process.sleep(@wait_time * 3) + defp wait(_, {:message_queue_len, 0}), do: Process.sleep(@wait_time * 4) # Exit recursive loop (pid is dead) - defp wait(_, nil), do: Process.sleep(@wait_time * 3) + defp wait(_, nil), do: Process.sleep(@wait_time * 4) # Continue recursive loop defp wait(pid, {:message_queue_len, _n}), do: check_on_mbox(pid) From 1aac649e9bd3d905ef9f718fe1e9d135e24ca425 Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Tue, 31 Mar 2020 14:15:25 -0500 Subject: [PATCH 12/14] Test case: AutoSyncHnadler Handles @no_cache_kinds --- .../farmbot_ext/amqp/auto_sync_asset_handler_test.exs | 10 ++++++++++ farmbot_ext/test/test_helper.exs | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/farmbot_ext/test/farmbot_ext/amqp/auto_sync_asset_handler_test.exs b/farmbot_ext/test/farmbot_ext/amqp/auto_sync_asset_handler_test.exs index ac80ac79..b4210edd 100644 --- a/farmbot_ext/test/farmbot_ext/amqp/auto_sync_asset_handler_test.exs +++ b/farmbot_ext/test/farmbot_ext/amqp/auto_sync_asset_handler_test.exs @@ -24,6 +24,16 @@ defmodule AutoSyncAssetHandlerTest do AutoSyncAssetHandler.handle_asset("Point", 23, nil) end + test "Handles @no_cache_kinds" do + id = 64 + params = %{} + kind = ~w(Device FbosConfig FirmwareConfig FarmwareEnv FarmwareInstallation) + |> Enum.shuffle + |> Enum.at(0) + expect(Asset.Command, :update, 1, fn ^kind, ^id, ^params -> :ok end) + assert :ok = AutoSyncAssetHandler.cache_sync(kind, id, params) + end + test "handling of deleted assets when auto_sync is enabled" do auto_sync_on() expect_sync_status_to_be("syncing") diff --git a/farmbot_ext/test/test_helper.exs b/farmbot_ext/test/test_helper.exs index 57554e4d..db51a065 100644 --- a/farmbot_ext/test/test_helper.exs +++ b/farmbot_ext/test/test_helper.exs @@ -23,7 +23,7 @@ defmodule Helpers do # Maybe I could use `start_supervised`? # https://hexdocs.pm/ex_unit/ExUnit.Callbacks.html#start_supervised/2 - @wait_time 30 + @wait_time 60 # Base case: We have a pid def wait_for(pid) when is_pid(pid), do: check_on_mbox(pid) # Failure case: We failed to find a pid for a module. @@ -38,9 +38,9 @@ defmodule Helpers do end # Exit recursive loop (mbox is clear) - defp wait(_, {:message_queue_len, 0}), do: Process.sleep(@wait_time * 4) + defp wait(_, {:message_queue_len, 0}), do: Process.sleep(@wait_time * 3) # Exit recursive loop (pid is dead) - defp wait(_, nil), do: Process.sleep(@wait_time * 4) + defp wait(_, nil), do: Process.sleep(@wait_time * 3) # Continue recursive loop defp wait(pid, {:message_queue_len, _n}), do: check_on_mbox(pid) From 53f28daefea05fb7bf46b2d7a090dc28688b7255 Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Tue, 31 Mar 2020 14:45:14 -0500 Subject: [PATCH 13/14] Finish testing AutoSyncAssetHandler --- farmbot_ext/config/test.exs | 1 + .../amqp/auto_sync_asset_handler_test.exs | 27 ++++++++++++++++-- farmbot_ext/test/test_helper.exs | 28 +++++++++++-------- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/farmbot_ext/config/test.exs b/farmbot_ext/config/test.exs index a6cd3dfe..54a47b28 100644 --- a/farmbot_ext/config/test.exs +++ b/farmbot_ext/config/test.exs @@ -1,6 +1,7 @@ use Mix.Config if Mix.env() == :test do + config :ex_unit, capture_logs: true mapper = fn mod -> config :farmbot_ext, mod, children: [] end list = [ diff --git a/farmbot_ext/test/farmbot_ext/amqp/auto_sync_asset_handler_test.exs b/farmbot_ext/test/farmbot_ext/amqp/auto_sync_asset_handler_test.exs index b4210edd..3f619a28 100644 --- a/farmbot_ext/test/farmbot_ext/amqp/auto_sync_asset_handler_test.exs +++ b/farmbot_ext/test/farmbot_ext/amqp/auto_sync_asset_handler_test.exs @@ -1,4 +1,5 @@ defmodule AutoSyncAssetHandlerTest do + require Helpers use ExUnit.Case, async: false use Mimic @@ -8,6 +9,8 @@ defmodule AutoSyncAssetHandlerTest do alias FarmbotExt.AMQP.AutoSyncAssetHandler alias FarmbotCore.{Asset, BotState, Leds} + import ExUnit.CaptureLog + def auto_sync_off, do: expect(Asset.Query, :auto_sync?, fn -> false end) def auto_sync_on, do: expect(Asset.Query, :auto_sync?, fn -> true end) @@ -27,9 +30,12 @@ defmodule AutoSyncAssetHandlerTest do test "Handles @no_cache_kinds" do id = 64 params = %{} - kind = ~w(Device FbosConfig FirmwareConfig FarmwareEnv FarmwareInstallation) - |> Enum.shuffle - |> Enum.at(0) + + kind = + ~w(Device FbosConfig FirmwareConfig FarmwareEnv FarmwareInstallation) + |> Enum.shuffle() + |> Enum.at(0) + expect(Asset.Command, :update, 1, fn ^kind, ^id, ^params -> :ok end) assert :ok = AutoSyncAssetHandler.cache_sync(kind, id, params) end @@ -42,4 +48,19 @@ defmodule AutoSyncAssetHandlerTest do expect_green_leds(:solid) AutoSyncAssetHandler.handle_asset("Point", 32, nil) end + + test "cache sync" do + id = 64 + params = %{} + kind = "Point" + # Helpers.expect_log("Autocaching sync #{kind} #{id} #{inspect(params)}") + changeset = %{ab: :cd} + changesetfaker = fn ^kind, ^id, ^params -> changeset end + expect(FarmbotCore.Asset.Command, :new_changeset, 1, changesetfaker) + expect(FarmbotExt.API.EagerLoader, :cache, 1, fn ^changeset -> :ok end) + expect_sync_status_to_be("sync_now") + expect_green_leds(:slow_blink) + do_it = fn -> AutoSyncAssetHandler.cache_sync(kind, id, params) end + assert capture_log(do_it) =~ "Autocaching sync Point 64 %{}" + end end diff --git a/farmbot_ext/test/test_helper.exs b/farmbot_ext/test/test_helper.exs index db51a065..afe7455f 100644 --- a/farmbot_ext/test/test_helper.exs +++ b/farmbot_ext/test/test_helper.exs @@ -1,17 +1,21 @@ Application.ensure_all_started(:farmbot) -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) +[ + AMQP.Channel, + FarmbotCeleryScript.SysCalls.Stubs, + FarmbotCore.Asset.Command, + FarmbotCore.Asset.Query, + FarmbotCore.BotState, + FarmbotCore.Leds, + FarmbotCore.LogExecutor, + FarmbotExt.AMQP.AutoSyncAssetHandler, + FarmbotExt.AMQP.ConnectionWorker, + FarmbotExt.API, + FarmbotExt.API.EagerLoader, + FarmbotExt.API.EagerLoader.Supervisor, + FarmbotExt.API.Preloader, +] +|> Enum.map(&Mimic.copy/1) timeout = System.get_env("EXUNIT_TIMEOUT") || "5000" System.put_env("LOG_SILENCE", "true") From 0c4b14eb1bec8d466fbb815bfd7ee13b0b2d8c91 Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Tue, 31 Mar 2020 14:49:24 -0500 Subject: [PATCH 14/14] mix format --- farmbot_ext/test/test_helper.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/farmbot_ext/test/test_helper.exs b/farmbot_ext/test/test_helper.exs index afe7455f..40f2c1b8 100644 --- a/farmbot_ext/test/test_helper.exs +++ b/farmbot_ext/test/test_helper.exs @@ -13,7 +13,7 @@ Application.ensure_all_started(:farmbot) FarmbotExt.API, FarmbotExt.API.EagerLoader, FarmbotExt.API.EagerLoader.Supervisor, - FarmbotExt.API.Preloader, + FarmbotExt.API.Preloader ] |> Enum.map(&Mimic.copy/1)