From 9c5fe755553263a69d5d0c0c128a4eeb9b51cfe6 Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Thu, 25 Apr 2024 19:26:39 +1200 Subject: [PATCH] the `Client` behaviour can now be used with `use` this allows for people to use the defaults, and override the implementations they want without having to implement all of them. the `ClientTest` no longer really makes sense, so I've removed it --- README.md | 13 +- config/test.exs | 2 +- lib/nerves_hub_link/application.ex | 5 +- lib/nerves_hub_link/archive_manager.ex | 8 +- lib/nerves_hub_link/client.ex | 209 +++++++++---------- lib/nerves_hub_link/client/default.ex | 75 +------ lib/nerves_hub_link/configurator.ex | 4 +- lib/nerves_hub_link/socket.ex | 13 +- mix.exs | 1 - mix.lock | 1 - test/nerves_hub_link/client/default_test.exs | 20 +- test/nerves_hub_link/client_test.exs | 59 ------ test/support/mocks.ex | 1 - test/support/test_client.ex | 9 + 14 files changed, 142 insertions(+), 278 deletions(-) delete mode 100644 test/nerves_hub_link/client_test.exs delete mode 100644 test/support/mocks.ex create mode 100644 test/support/test_client.ex diff --git a/README.md b/README.md index 7670cdf..4b45275 100644 --- a/README.md +++ b/README.md @@ -308,13 +308,15 @@ mix nerves_hub.firmware publish --key devkey --deploy qa_deployment ### Conditionally applying updates -It's not always appropriate to apply a firmware update immediately. Custom logic can be added to the device by implementing the `NervesHubLink.Client` behaviour and telling the NervesHubLink OTP application about it. +It's not always appropriate to apply a firmware update immediately. Custom logic can be added to the device by implementing the `NervesHubLink.Client` behaviour, or extending `NervesHubLink.Client`, and telling the `NervesHubLink` OTP application to use it. + +__It's recommended to extend `NervesHubLink.Client` by using `use NervesHubLink.Client` as this will allow you to override individual functions instead of needing to implement the entire behaviour.__ Here's an example implementation: ```elixir defmodule MyApp.NervesHubLinkClient do - @behaviour NervesHubLink.Client + use NervesHubLink.Client # May return: # * `:apply` - apply the action immediately @@ -344,7 +346,8 @@ See the previous section for implementing a `client` behaviour. ```elixir defmodule MyApp.NervesHubLinkClient do - @behaviour NervesHubLink.Client + use NervesHubLink.Client + # argument can be: # {:ok, non_neg_integer(), String.t()} # {:warning, non_neg_integer(), String.t()} @@ -392,14 +395,14 @@ config :nerves_hub_link, remote_iex_timeout: 900 # 15 minutes You may also need additional permissions on NervesHub to see the device and to use the remote IEx feature. -### Alarms +### Alarms This application can set and clear the following alarms: * `NervesHubLink.Disconnected` * set: An issue is preventing a connection to NervesHub or one just hasn't been made yet * clear: Currently connected to NervesHub -* `NervesHubLink.UpdateInProgress` +* `NervesHubLink.UpdateInProgress` * set: A new firmware update is being downloaded or applied * clear: No updates are happening diff --git a/config/test.exs b/config/test.exs index cf2c939..62006e1 100644 --- a/config/test.exs +++ b/config/test.exs @@ -17,7 +17,7 @@ config :nerves_hub_link, ] config :nerves_hub_link, - client: NervesHubLink.ClientMock, + client: NervesHubLink.Support.TestClient, rejoin_after: 0, remote_iex: true diff --git a/lib/nerves_hub_link/application.ex b/lib/nerves_hub_link/application.ex index 6e354a3..d07b539 100644 --- a/lib/nerves_hub_link/application.ex +++ b/lib/nerves_hub_link/application.ex @@ -2,7 +2,6 @@ defmodule NervesHubLink.Application do use Application alias NervesHubLink.ArchiveManager - alias NervesHubLink.Client alias NervesHubLink.Configurator alias NervesHubLink.Socket alias NervesHubLink.FwupConfig @@ -15,8 +14,8 @@ defmodule NervesHubLink.Application do fwup_public_keys: config.fwup_public_keys, fwup_devpath: config.fwup_devpath, fwup_env: config.fwup_env, - handle_fwup_message: &Client.handle_fwup_message/1, - update_available: &Client.update_available/1 + handle_fwup_message: &config.client.handle_fwup_message/1, + update_available: &config.client.update_available/1 } children = children(config, fwup_config) diff --git a/lib/nerves_hub_link/archive_manager.ex b/lib/nerves_hub_link/archive_manager.ex index 36ba14a..dff4ea6 100644 --- a/lib/nerves_hub_link/archive_manager.ex +++ b/lib/nerves_hub_link/archive_manager.ex @@ -12,7 +12,6 @@ defmodule NervesHubLink.ArchiveManager do use GenServer - alias NervesHubLink.Client alias NervesHubLink.Downloader alias NervesHubLink.Message.ArchiveInfo @@ -23,6 +22,7 @@ defmodule NervesHubLink.ArchiveManager do @type t :: %__MODULE__{ archive_info: nil | ArchiveInfo.t(), archive_public_keys: [binary()], + client: NervesHubLink.Client, data_path: Path.t(), download: nil | GenServer.server(), file_path: Path.t(), @@ -33,6 +33,7 @@ defmodule NervesHubLink.ArchiveManager do defstruct archive_info: nil, archive_public_keys: [], + client: nil, data_path: nil, download: nil, file_path: nil, @@ -84,6 +85,7 @@ defmodule NervesHubLink.ArchiveManager do def init(args) do state = %__MODULE__{ archive_public_keys: args.archive_public_keys, + client: args.client, data_path: args.data_path } @@ -124,7 +126,7 @@ defmodule NervesHubLink.ArchiveManager do # validate the file if valid_archive?(state.file_path, state.archive_public_keys) do - _ = Client.archive_ready(state.archive_info, state.file_path) + _ = state.client.archive_ready(state.archive_info, state.file_path) else Logger.error( "[NervesHubLink] Archive could not be validated, your public keys are configured wrong" @@ -171,7 +173,7 @@ defmodule NervesHubLink.ArchiveManager do pid = self() - case Client.archive_available(info) do + case state.client.archive_available(info) do :download -> {:ok, download} = Downloader.start_download(info.url, &send(pid, {:download, &1})) diff --git a/lib/nerves_hub_link/client.ex b/lib/nerves_hub_link/client.ex index b6f8810..3d6fd82 100644 --- a/lib/nerves_hub_link/client.ex +++ b/lib/nerves_hub_link/client.ex @@ -1,17 +1,24 @@ defmodule NervesHubLink.Client do @moduledoc """ - A behaviour module for customizing if and when firmware updates get applied. + A behaviour module for customizing: + - if and when firmware updates get applied + - if and when archives get applied + - reconnection backoff logic + - and customizing how a device is identified and rebooted By default NervesHubLink applies updates as soon as it knows about them from the NervesHubLink server and doesn't give warning before rebooting. This let's devices hook into the decision making process and monitor the update's progress. + You can either implement all the callbacks for the `NervesHubLink.Client` behaviour, + or you can `use NervesHubLink.Client` and override the default implementation. + # Example ```elixir defmodule MyApp.NervesHubLinkClient do - @behaviour NervesHubLink.Client + use NervesHubLink.Client # May return: # * `:apply` - apply the action immediately @@ -99,10 +106,7 @@ defmodule NervesHubLink.Client do @callback handle_error(any()) :: :ok @doc """ - Optional callback when the socket disconnected, before starting to reconnect. - - The return value is used to reset the next socket's retry timeout. `nil` uses - the default. The default is a call to `NervesHubLink.Backoff.delay_list/3`. + Callback when the socket disconnected, before starting to reconnect. You may wish to use this to dynamically change the reconnect backoffs. For instance, during a NervesHub deploy you may wish to change the reconnect based on your @@ -118,139 +122,116 @@ defmodule NervesHubLink.Client do @callback identify() :: :ok @doc """ - Optional callback to reboot the device when a firmware update completes - - The default behavior is to call `Nerves.Runtime.reboot/0` after a successful update. This - is useful for testing and for doing additional work like notifying users in a UI that a reboot - will happen soon. It is critical that a reboot does happen. + Callback to reboot the device when a firmware update completes """ @callback reboot() :: no_return() - @optional_callbacks [reconnect_backoff: 0, reboot: 0] + defmacro __using__(_opts) do + quote do + @behaviour NervesHubLink.Client + require Logger - @doc """ - This function is called internally by NervesHubLink to notify clients. - """ - @spec update_available(update_data()) :: update_response() - def update_available(data) do - case apply_wrap(mod(), :update_available, [data]) do - :apply -> - :apply + @impl true + def update_available(update_info) do + if update_info.firmware_meta.uuid == Nerves.Runtime.KV.get_active("nerves_fw_uuid") do + Logger.info(""" + [NervesHubLink.Client] Ignoring request to update to the same firmware - :ignore -> - :ignore + #{inspect(update_info)} + """) - {:reschedule, timeout} when timeout > 0 -> - {:reschedule, timeout} + :ignore + else + :apply + end + end - wrong -> - Logger.error( - "[NervesHubLink] Client: #{inspect(mod())}.update_available/1 bad return value: #{inspect(wrong)} Applying update." + @impl true + def archive_available(archive_info) do + Logger.info( + "[NervesHubLink.Client] Archive is available for downloading #{inspect(archive_info)}" ) - :apply - end - end - - @spec archive_available(archive_data()) :: archive_response() - def archive_available(data) do - apply_wrap(mod(), :archive_available, [data]) - end - - @spec archive_ready(archive_data(), Path.t()) :: :ok - def archive_ready(data, file_path) do - _ = apply_wrap(mod(), :archive_ready, [data, file_path]) + :ignore + end - :ok - end + @impl true + def archive_ready(archive_info, file_path) do + Logger.info( + "[NervesHubLink.Client] Archive is ready for processing #{inspect(archive_info)} at #{inspect(file_path)}" + ) - @doc """ - This function is called internally by NervesHubLink to notify clients of fwup progress. - """ - @spec handle_fwup_message(fwup_message()) :: :ok - def handle_fwup_message(data) do - _ = apply_wrap(mod(), :handle_fwup_message, [data]) + :ok + end - # TODO: nasty side effects here. Consider moving somewhere else - case data do - {:progress, percent} -> + @impl true + def handle_fwup_message({:progress, percent}) do + Logger.debug("FWUP PROG: #{percent}%") NervesHubLink.send_update_progress(percent) + :ok + end - {:error, _, message} -> + def handle_fwup_message({:error, _, message}) do + Logger.error("FWUP ERROR: #{message}") NervesHubLink.send_update_status("fwup error #{message}") - - {:ok, 0, _message} -> - initiate_reboot() - - _ -> :ok - end - end + end - @doc """ - This function is called internally by NervesHubLink to identify a device. - """ - def identify() do - apply_wrap(mod(), :identify, []) - end + def handle_fwup_message({:warning, _, message}) do + Logger.warning("FWUP WARN: #{message}") + :ok + end - @doc """ - This function is called internally by NervesHubLink to initiate a reboot. + def handle_fwup_message({:ok, status, message}) do + Logger.info("FWUP SUCCESS: #{status} #{message}") + reboot() + :ok + end - After a successful firmware update, NervesHubLink calls this to start the - reboot process. It calls `c:reboot/0` if supplied or - `Nerves.Runtime.reboot/0`. - """ - @spec initiate_reboot() :: :ok - def initiate_reboot() do - client = mod() + def handle_fwup_message(fwup_message) do + Logger.warning("Unknown FWUP message: #{inspect(fwup_message)}") + :ok + end - {mod, fun, args} = - if function_exported?(client, :reboot, 0), - do: {client, :reboot, []}, - else: {Nerves.Runtime, :reboot, []} + @impl true + def handle_error(error) do + Logger.warning("[NervesHubLink] error: #{inspect(error)}") + end - _ = spawn(mod, fun, args) - :ok - end + @doc """ + The default implementation checks if the `:reconnect_after_msec` config has been + configured, and is a list of values, otherwise `NervesHubLink.Backoff.delay_list/3` + is used with a minimum value of 1 second, maximum value of 60 seconds, and a 50% jitter. + """ + @impl true + def reconnect_backoff() do + socket_config = Application.get_env(:nerves_hub_link, :socket, []) + + backoff = socket_config[:reconnect_after_msec] + + if is_list(backoff) do + backoff + else + NervesHubLink.Backoff.delay_list(1000, 60000, 0.50) + end + end - @doc """ - This function is called internally by NervesHubLink to notify clients of fwup errors. - """ - @spec handle_error(any()) :: :ok - def handle_error(data) do - _ = apply_wrap(mod(), :handle_error, [data]) - end + @doc """ + The default implementation calls `Nerves.Runtime.reboot/0` after a successful update. This + is useful for testing and for doing additional work like notifying users in a UI that a reboot + will happen soon. It is critical that a reboot does happen. + """ + @impl true + def reboot() do + _ = spawn(Nerves.Runtime, :reboot, []) + end - @doc """ - This function is called internally by NervesHubLink to notify clients of disconnects. - """ - @spec reconnect_backoff() :: [integer()] - def reconnect_backoff() do - backoff = - if function_exported?(mod(), :reconnect_backoff, 0) do - apply_wrap(mod(), :reconnect_backoff, []) - else - nil + @impl true + def identify() do + Logger.info("[NervesHubLink] identifying") end - if is_list(backoff) do - backoff - else - NervesHubLink.Backoff.delay_list(1000, 60000, 0.50) + defoverridable NervesHubLink.Client end end - - # Catches exceptions and exits - defp apply_wrap(mod, function, args) do - apply(mod, function, args) - catch - :error, reason -> {:error, reason} - :exit, reason -> {:exit, reason} - err -> err - end - - defp mod() do - Application.get_env(:nerves_hub_link, :client, NervesHubLink.Client.Default) - end end diff --git a/lib/nerves_hub_link/client/default.ex b/lib/nerves_hub_link/client/default.ex index a7dfa48..08a59fd 100644 --- a/lib/nerves_hub_link/client/default.ex +++ b/lib/nerves_hub_link/client/default.ex @@ -1,80 +1,7 @@ defmodule NervesHubLink.Client.Default do @moduledoc """ Default NervesHubLink.Client implementation - - This client always accepts an update. """ - @behaviour NervesHubLink.Client - require Logger - - @impl NervesHubLink.Client - def update_available(update_info) do - if update_info.firmware_meta.uuid == Nerves.Runtime.KV.get_active("nerves_fw_uuid") do - Logger.info(""" - [NervesHubLink.Client] Ignoring request to update to the same firmware - - #{inspect(update_info)} - """) - - :ignore - else - :apply - end - end - - @impl NervesHubLink.Client - def archive_available(archive_info) do - Logger.info( - "[NervesHubLink.Client] Archive is available for downloading #{inspect(archive_info)}" - ) - - :ignore - end - - @impl NervesHubLink.Client - def archive_ready(archive_info, file_path) do - Logger.info( - "[NervesHubLink.Client] Archive is ready for processing #{inspect(archive_info)} at #{inspect(file_path)}" - ) - - :ok - end - - @impl NervesHubLink.Client - def handle_fwup_message({:progress, percent}) do - Logger.debug("FWUP PROG: #{percent}%") - end - - def handle_fwup_message({:error, _, message}) do - Logger.error("FWUP ERROR: #{message}") - end - - def handle_fwup_message({:warning, _, message}) do - Logger.warning("FWUP WARN: #{message}") - end - - def handle_fwup_message({:ok, status, message}) do - Logger.info("FWUP SUCCESS: #{status} #{message}") - end - - def handle_fwup_message(fwup_message) do - Logger.warning("Unknown FWUP message: #{inspect(fwup_message)}") - end - - @impl NervesHubLink.Client - def handle_error(error) do - Logger.warning("[NervesHubLink] error: #{inspect(error)}") - end - - @impl NervesHubLink.Client - def reconnect_backoff() do - socket_config = Application.get_env(:nerves_hub_link, :socket, []) - socket_config[:reconnect_after_msec] - end - - @impl NervesHubLink.Client - def identify() do - Logger.info("[NervesHubLink] identifying") - end + use NervesHubLink.Client end diff --git a/lib/nerves_hub_link/configurator.ex b/lib/nerves_hub_link/configurator.ex index f1b36d0..65d6552 100644 --- a/lib/nerves_hub_link/configurator.ex +++ b/lib/nerves_hub_link/configurator.ex @@ -7,7 +7,8 @@ defmodule NervesHubLink.Configurator do @console_version "2.0.0" defmodule Config do - defstruct connect: true, + defstruct client: NervesHubLink.Client.Default, + connect: true, data_path: "/data/nerves-hub", device_api_host: nil, device_api_port: 443, @@ -24,6 +25,7 @@ defmodule NervesHubLink.Configurator do ssl: [] @type t() :: %__MODULE__{ + client: NervesHubLink.Client, connect: boolean(), data_path: Path.t(), device_api_host: String.t(), diff --git a/lib/nerves_hub_link/socket.ex b/lib/nerves_hub_link/socket.ex index 3d2dfe2..aeb0e68 100644 --- a/lib/nerves_hub_link/socket.ex +++ b/lib/nerves_hub_link/socket.ex @@ -6,7 +6,6 @@ defmodule NervesHubLink.Socket do require Logger alias NervesHubLink.ArchiveManager - alias NervesHubLink.Client alias NervesHubLink.Configurator.SharedSecret alias NervesHubLink.UpdateManager alias NervesHubLink.UploadFile @@ -267,7 +266,7 @@ defmodule NervesHubLink.Socket do end def handle_message(@device_topic, "identify", _params, socket) do - Client.identify() + socket.assigns.client.identify() {:ok, socket} end @@ -408,7 +407,7 @@ defmodule NervesHubLink.Socket do @impl Slipstream def handle_topic_close(topic, reason, socket) when reason != :left do if topic == @device_topic do - _ = Client.handle_error(reason) + _ = socket.assigns.client.handle_error(reason) end rejoin(socket, topic, socket.assigns.params) @@ -416,9 +415,13 @@ defmodule NervesHubLink.Socket do @impl Slipstream def handle_disconnect(reason, socket) do - _ = Client.handle_error(reason) + _ = socket.assigns.client.handle_error(reason) :alarm_handler.set_alarm({NervesHubLink.Disconnected, [reason: reason]}) - channel_config = %{socket.channel_config | reconnect_after_msec: Client.reconnect_backoff()} + + channel_config = %{ + socket.channel_config + | reconnect_after_msec: socket.assigns.client.reconnect_backoff() + } channel_config = case Application.get_env(:nerves_hub_link, :configurator) do diff --git a/mix.exs b/mix.exs index dee07bc..37537db 100644 --- a/mix.exs +++ b/mix.exs @@ -96,7 +96,6 @@ defmodule NervesHubLink.MixProject do {:fwup, "~> 1.0"}, {:jason, "~> 1.0"}, {:mint, "~> 1.2"}, - {:mox, "~> 1.0", only: :test}, {:nerves_key, "~> 1.0 or ~> 0.5", optional: true}, {:nerves_runtime, "~> 0.8"}, {:nerves_time, "~> 0.4"}, diff --git a/mix.lock b/mix.lock index 349ede1..f146348 100644 --- a/mix.lock +++ b/mix.lock @@ -24,7 +24,6 @@ "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, "mint": {:hex, :mint, "1.5.2", "4805e059f96028948870d23d7783613b7e6b0e2fb4e98d720383852a760067fd", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "d77d9e9ce4eb35941907f1d3df38d8f750c357865353e21d335bdcdf6d892a02"}, "mint_web_socket": {:hex, :mint_web_socket, "1.0.3", "aab42fff792a74649916236d0b01f560a0b3f03ca5dea693c230d1c44736b50e", [:mix], [{:mint, ">= 1.4.1 and < 2.0.0-0", [hex: :mint, repo: "hexpm", optional: false]}], "hexpm", "ca3810ca44cc8532e3dce499cc17f958596695d226bb578b2fbb88c09b5954b0"}, - "mox": {:hex, :mox, "1.1.0", "0f5e399649ce9ab7602f72e718305c0f9cdc351190f72844599545e4996af73c", [:mix], [], "hexpm", "d44474c50be02d5b72131070281a5d3895c0e7a95c780e90bc0cfe712f633a13"}, "muontrap": {:hex, :muontrap, "1.5.0", "bf5c273872379968615a39974458328209ac97fa1f588396192131ff973d1ca2", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "daf605e877f60b5be9215e3420d7971fc468677b29921e40915b15fd928273d4"}, "nerves_key": {:hex, :nerves_key, "1.2.0", "2cd65b7f474582d0af7bcd59664df16d0c58d1673dce573072d7dbe971dbd663", [:mix], [{:atecc508a, "~> 0.3.0 or ~> 1.1", [hex: :atecc508a, repo: "hexpm", optional: false]}, {:nerves_key_pkcs11, "~> 0.2 or ~> 1.0", [hex: :nerves_key_pkcs11, repo: "hexpm", optional: false]}], "hexpm", "ef49b26f38728b554cca2a0b16d198989a571e753d427ead2b22a8ea10a06000"}, "nerves_key_pkcs11": {:hex, :nerves_key_pkcs11, "1.2.0", "73fa6796a515de9d4feaad9bc68b2c63b1e5ba9cb32328fa2b349b896a2d18da", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "d4e515edb8838d0de7776ca920cc0e5b6767de95c15070979492d0315b72de19"}, diff --git a/test/nerves_hub_link/client/default_test.exs b/test/nerves_hub_link/client/default_test.exs index a52d053..39495cd 100644 --- a/test/nerves_hub_link/client/default_test.exs +++ b/test/nerves_hub_link/client/default_test.exs @@ -1,9 +1,9 @@ defmodule NervesHubLink.Client.DefaultTest do use ExUnit.Case, async: true - alias NervesHubLink.Client.Default + alias NervesHubLink.Support.TestClient alias NervesHubLink.Message.{FirmwareMetadata, UpdateInfo} - doctest Default + doctest TestClient @update_info %UpdateInfo{ firmware_url: "https://nerves-hub.org/firmware/1234", @@ -11,39 +11,39 @@ defmodule NervesHubLink.Client.DefaultTest do } test "update_available/1" do - assert Default.update_available(@update_info) == :apply + assert TestClient.update_available(@update_info) == :apply end test "update_available with same uuid" do update_info = put_in(@update_info.firmware_meta.uuid, Nerves.Runtime.KV.get_active("nerves_fw_uuid")) - assert Default.update_available(update_info) == :ignore + assert TestClient.update_available(update_info) == :ignore end describe "handle_fwup_message/1" do test "progress" do - assert Default.handle_fwup_message({:progress, 25}) == :ok + assert TestClient.handle_fwup_message({:progress, 25}) == :ok end test "error" do - assert Default.handle_fwup_message({:error, :any, "message"}) == :ok + assert TestClient.handle_fwup_message({:error, :any, "message"}) == :ok end test "warning" do - assert Default.handle_fwup_message({:warning, :any, "message"}) == :ok + assert TestClient.handle_fwup_message({:warning, :any, "message"}) == :ok end test "completion" do - assert Default.handle_fwup_message({:ok, 0, "success"}) == :ok + assert TestClient.handle_fwup_message({:ok, 0, "success"}) == :ok end test "any" do - assert Default.handle_fwup_message(:any) == :ok + assert TestClient.handle_fwup_message(:any) == :ok end end test "handle_error/1" do - assert Default.handle_error(:error) == :ok + assert TestClient.handle_error(:error) == :ok end end diff --git a/test/nerves_hub_link/client_test.exs b/test/nerves_hub_link/client_test.exs deleted file mode 100644 index b51ed78..0000000 --- a/test/nerves_hub_link/client_test.exs +++ /dev/null @@ -1,59 +0,0 @@ -defmodule NervesHubLink.ClientTest do - use ExUnit.Case, async: true - alias NervesHubLink.{Client, ClientMock} - - @compile {:no_warn_undefined, {Not, :real, 0}} - @compile {:no_warn_undefined, {:something, :exception, 1}} - - doctest Client - - setup context do - Mox.verify_on_exit!(context) - end - - test "update_available/2" do - Mox.expect(ClientMock, :update_available, fn :data -> :apply end) - assert Client.update_available(:data) == :apply - - Mox.expect(ClientMock, :update_available, fn :data -> :wrong end) - assert Client.update_available(:data) == :apply - - Mox.expect(ClientMock, :update_available, fn :data -> :ignore end) - assert Client.update_available(:data) == :ignore - - Mox.expect(ClientMock, :update_available, fn :data -> {:reschedule, 1337} end) - assert Client.update_available(:data) == {:reschedule, 1337} - end - - test "handle_fwup_message/2" do - Mox.expect(ClientMock, :handle_fwup_message, fn :data -> :ok end) - assert Client.handle_fwup_message(:data) == :ok - end - - test "handle_error/2" do - Mox.expect(ClientMock, :handle_error, fn :data -> :ok end) - assert Client.handle_error(:data) == :ok - end - - describe "apply_wrap doesn't propagate failures" do - test "error" do - Mox.expect(ClientMock, :handle_fwup_message, fn :data -> raise :something end) - assert Client.handle_fwup_message(:data) == :ok - end - - test "exit" do - Mox.expect(ClientMock, :handle_fwup_message, fn :data -> exit(:reason) end) - assert Client.handle_fwup_message(:data) == :ok - end - - test "throw" do - Mox.expect(ClientMock, :handle_fwup_message, fn :data -> throw(:reason) end) - assert Client.handle_fwup_message(:data) == :ok - end - - test "exception" do - Mox.expect(ClientMock, :handle_fwup_message, fn :data -> Not.real() end) - assert Client.handle_fwup_message(:data) == :ok - end - end -end diff --git a/test/support/mocks.ex b/test/support/mocks.ex deleted file mode 100644 index 2c375ca..0000000 --- a/test/support/mocks.ex +++ /dev/null @@ -1 +0,0 @@ -Mox.defmock(NervesHubLink.ClientMock, for: NervesHubLink.Client) diff --git a/test/support/test_client.ex b/test/support/test_client.ex new file mode 100644 index 0000000..24eb7ee --- /dev/null +++ b/test/support/test_client.ex @@ -0,0 +1,9 @@ +defmodule NervesHubLink.Support.TestClient do + @moduledoc """ + Default NervesHubLink.Client implementation + """ + + use NervesHubLink.Client + + def reboot(), do: nil +end