diff --git a/openfeature/providers/elixir-provider/.credo.exs b/openfeature/providers/elixir-provider/.credo.exs new file mode 100644 index 00000000000..baa1ea24410 --- /dev/null +++ b/openfeature/providers/elixir-provider/.credo.exs @@ -0,0 +1,217 @@ +# This file contains the configuration for Credo and you are probably reading +# this after creating it with `mix credo.gen.config`. +# +# If you find anything wrong or unclear in this file, please report an +# issue on GitHub: https://github.com/rrrene/credo/issues +# +%{ + # + # You can have as many configs as you like in the `configs:` field. + configs: [ + %{ + # + # Run any config using `mix credo -C `. If no config name is given + # "default" is used. + # + name: "default", + # + # These are the files included in the analysis: + files: %{ + # + # You can give explicit globs or simply directories. + # In the latter case `**/*.{ex,exs}` will be used. + # + included: [ + "lib/", + "src/", + "test/", + "web/", + "apps/*/lib/", + "apps/*/src/", + "apps/*/test/", + "apps/*/web/" + ], + excluded: [~r"/_build/", ~r"/deps/", ~r"/node_modules/"] + }, + # + # Load and configure plugins here: + # + plugins: [], + # + # If you create your own checks, you must specify the source files for + # them here, so they can be loaded by Credo before running the analysis. + # + requires: [], + # + # If you want to enforce a style guide and need a more traditional linting + # experience, you can change `strict` to `true` below: + # + strict: false, + # + # To modify the timeout for parsing files, change this value: + # + parse_timeout: 5000, + # + # If you want to use uncolored output by default, you can change `color` + # to `false` below: + # + color: true, + # + # You can customize the parameters of any check by adding a second element + # to the tuple. + # + # To disable a check put `false` as second element: + # + # {Credo.Check.Design.DuplicatedCode, false} + # + checks: %{ + enabled: [ + # + ## Consistency Checks + # + {Credo.Check.Consistency.ExceptionNames, []}, + {Credo.Check.Consistency.LineEndings, []}, + {Credo.Check.Consistency.ParameterPatternMatching, []}, + {Credo.Check.Consistency.SpaceAroundOperators, []}, + {Credo.Check.Consistency.SpaceInParentheses, []}, + {Credo.Check.Consistency.TabsOrSpaces, []}, + + # + ## Design Checks + # + # You can customize the priority of any check + # Priority values are: `low, normal, high, higher` + # + {Credo.Check.Design.AliasUsage, + [priority: :low, if_nested_deeper_than: 2, if_called_more_often_than: 0]}, + {Credo.Check.Design.TagFIXME, []}, + # You can also customize the exit_status of each check. + # If you don't want TODO comments to cause `mix credo` to fail, just + # set this value to 0 (zero). + # + {Credo.Check.Design.TagTODO, [exit_status: 2]}, + + # + ## Readability Checks + # + {Credo.Check.Readability.AliasOrder, []}, + {Credo.Check.Readability.FunctionNames, []}, + {Credo.Check.Readability.LargeNumbers, []}, + {Credo.Check.Readability.MaxLineLength, [priority: :low, max_length: 120]}, + {Credo.Check.Readability.ModuleAttributeNames, []}, + {Credo.Check.Readability.ModuleDoc, []}, + {Credo.Check.Readability.ModuleNames, []}, + {Credo.Check.Readability.ParenthesesInCondition, []}, + {Credo.Check.Readability.ParenthesesOnZeroArityDefs, []}, + {Credo.Check.Readability.PipeIntoAnonymousFunctions, []}, + {Credo.Check.Readability.PredicateFunctionNames, []}, + {Credo.Check.Readability.PreferImplicitTry, []}, + {Credo.Check.Readability.RedundantBlankLines, []}, + {Credo.Check.Readability.Semicolons, []}, + {Credo.Check.Readability.SpaceAfterCommas, []}, + {Credo.Check.Readability.StringSigils, []}, + {Credo.Check.Readability.TrailingBlankLine, []}, + {Credo.Check.Readability.TrailingWhiteSpace, []}, + {Credo.Check.Readability.UnnecessaryAliasExpansion, []}, + {Credo.Check.Readability.VariableNames, []}, + {Credo.Check.Readability.WithSingleClause, []}, + + # + ## Refactoring Opportunities + # + {Credo.Check.Refactor.Apply, []}, + {Credo.Check.Refactor.CondStatements, []}, + {Credo.Check.Refactor.CyclomaticComplexity, []}, + {Credo.Check.Refactor.FilterCount, []}, + {Credo.Check.Refactor.FilterFilter, []}, + {Credo.Check.Refactor.FunctionArity, []}, + {Credo.Check.Refactor.LongQuoteBlocks, []}, + {Credo.Check.Refactor.MapJoin, []}, + {Credo.Check.Refactor.MatchInCondition, []}, + {Credo.Check.Refactor.NegatedConditionsInUnless, []}, + {Credo.Check.Refactor.NegatedConditionsWithElse, []}, + {Credo.Check.Refactor.Nesting, []}, + {Credo.Check.Refactor.RedundantWithClauseResult, []}, + {Credo.Check.Refactor.RejectReject, []}, + {Credo.Check.Refactor.UnlessWithElse, []}, + {Credo.Check.Refactor.WithClauses, []}, + + # + ## Warnings + # + {Credo.Check.Warning.ApplicationConfigInModuleAttribute, []}, + {Credo.Check.Warning.BoolOperationOnSameValues, []}, + {Credo.Check.Warning.Dbg, []}, + {Credo.Check.Warning.ExpensiveEmptyEnumCheck, []}, + {Credo.Check.Warning.IExPry, []}, + {Credo.Check.Warning.IoInspect, []}, + {Credo.Check.Warning.MissedMetadataKeyInLoggerConfig, []}, + {Credo.Check.Warning.OperationOnSameValues, []}, + {Credo.Check.Warning.OperationWithConstantResult, []}, + {Credo.Check.Warning.RaiseInsideRescue, []}, + {Credo.Check.Warning.SpecWithStruct, []}, + {Credo.Check.Warning.UnsafeExec, []}, + {Credo.Check.Warning.UnusedEnumOperation, []}, + {Credo.Check.Warning.UnusedFileOperation, []}, + {Credo.Check.Warning.UnusedKeywordOperation, []}, + {Credo.Check.Warning.UnusedListOperation, []}, + {Credo.Check.Warning.UnusedPathOperation, []}, + {Credo.Check.Warning.UnusedRegexOperation, []}, + {Credo.Check.Warning.UnusedStringOperation, []}, + {Credo.Check.Warning.UnusedTupleOperation, []}, + {Credo.Check.Warning.WrongTestFileExtension, []} + ], + disabled: [ + # + # Checks scheduled for next check update (opt-in for now) + {Credo.Check.Refactor.UtcNowTruncate, []}, + + # + # Controversial and experimental checks (opt-in, just move the check to `:enabled` + # and be sure to use `mix credo --strict` to see low priority checks) + # + {Credo.Check.Consistency.MultiAliasImportRequireUse, []}, + {Credo.Check.Consistency.UnusedVariableNames, []}, + {Credo.Check.Design.DuplicatedCode, []}, + {Credo.Check.Design.SkipTestWithoutComment, []}, + {Credo.Check.Readability.AliasAs, []}, + {Credo.Check.Readability.BlockPipe, []}, + {Credo.Check.Readability.ImplTrue, []}, + {Credo.Check.Readability.MultiAlias, []}, + {Credo.Check.Readability.NestedFunctionCalls, []}, + {Credo.Check.Readability.OneArityFunctionInPipe, []}, + {Credo.Check.Readability.OnePipePerLine, []}, + {Credo.Check.Readability.SeparateAliasRequire, []}, + {Credo.Check.Readability.SingleFunctionToBlockPipe, []}, + {Credo.Check.Readability.SinglePipe, []}, + {Credo.Check.Readability.Specs, []}, + {Credo.Check.Readability.StrictModuleLayout, []}, + {Credo.Check.Readability.WithCustomTaggedTuple, []}, + {Credo.Check.Refactor.ABCSize, []}, + {Credo.Check.Refactor.AppendSingleItem, []}, + {Credo.Check.Refactor.DoubleBooleanNegation, []}, + {Credo.Check.Refactor.FilterReject, []}, + {Credo.Check.Refactor.IoPuts, []}, + {Credo.Check.Refactor.MapMap, []}, + {Credo.Check.Refactor.ModuleDependencies, []}, + {Credo.Check.Refactor.NegatedIsNil, []}, + {Credo.Check.Refactor.PassAsyncInTestCases, []}, + {Credo.Check.Refactor.PipeChainStart, []}, + {Credo.Check.Refactor.RejectFilter, []}, + {Credo.Check.Refactor.VariableRebinding, []}, + {Credo.Check.Warning.LazyLogging, []}, + {Credo.Check.Warning.LeakyEnvironment, []}, + {Credo.Check.Warning.MapGetUnsafePass, []}, + {Credo.Check.Warning.MixEnv, []}, + {Credo.Check.Warning.UnsafeToAtom, []} + + # {Credo.Check.Refactor.MapInto, []}, + + # + # Custom checks can be created using `mix credo.gen.check`. + # + ] + } + } + ] +} diff --git a/openfeature/providers/elixir-provider/lib/provider/cache_controller.ex b/openfeature/providers/elixir-provider/lib/provider/cache_controller.ex index 3b63367db95..27889fb1f79 100644 --- a/openfeature/providers/elixir-provider/lib/provider/cache_controller.ex +++ b/openfeature/providers/elixir-provider/lib/provider/cache_controller.ex @@ -7,12 +7,13 @@ defmodule ElixirProvider.CacheController do @flag_table :flag_cache @spec start_link() :: GenServer.on_start() - def start_link() do - GenServer.start_link(__MODULE__, :ok, name: __MODULE__) + def start_link do + GenServer.start_link(__MODULE__, :ok, name: __MODULE__) end def get(flag_key, evaluation_hash) do cache_key = build_cache_key(flag_key, evaluation_hash) + case :ets.lookup(@flag_table, cache_key) do [{^cache_key, cached_value}] -> {:ok, cached_value} [] -> :miss diff --git a/openfeature/providers/elixir-provider/lib/provider/context_transformer.ex b/openfeature/providers/elixir-provider/lib/provider/context_transformer.ex index 23543d4f0cd..87bc1d272ea 100644 --- a/openfeature/providers/elixir-provider/lib/provider/context_transformer.ex +++ b/openfeature/providers/elixir-provider/lib/provider/context_transformer.ex @@ -6,29 +6,31 @@ defmodule ElixirProvider.ContextTransformer do alias OpenFeature.Types @doc """ - Finds any key-value pair with a non-nil value. + Extracts other key value pairs after the targeting key """ def get_any_value(map) when is_map(map) do - case Enum.find(map, fn {_key, value} -> value != nil end) do - {key, value} -> {:ok, {key, value}} - nil -> {:error, "No keys found with a value"} - end + map + |> Enum.reject(fn {key, _value} -> key === :targetingKey end) + |> Enum.into(%{}) end @doc """ Converts an EvaluationContext map into a ElixirProvider.GofEvaluationContext struct. Returns `{:ok, context}` on success, or `{:error, reason}` on failure. """ - @spec transform_context(Types.context()) :: {:ok, GofEvaluationContext.t()} | {:error, String.t()} + @spec transform_context(Types.context()) :: + {:ok, GofEvaluationContext.t()} | {:error, String.t()} def transform_context(ctx) do - case get_any_value(ctx) do - {:ok, {key, value}} -> - {:ok, %GofEvaluationContext{ - key: key, - custom: value - }} - {:error, reason} -> - {:error, reason} + case Map.fetch(ctx, :targetingKey) do + {:ok, value} -> + {:ok, + %GofEvaluationContext{ + key: value, + custom: get_any_value(ctx) + }} + + :error -> + {:error, "targeting key not found"} end end end diff --git a/openfeature/providers/elixir-provider/lib/provider/data_collector_hook.ex b/openfeature/providers/elixir-provider/lib/provider/data_collector_hook.ex index 10722424e72..722e85b5849 100644 --- a/openfeature/providers/elixir-provider/lib/provider/data_collector_hook.ex +++ b/openfeature/providers/elixir-provider/lib/provider/data_collector_hook.ex @@ -1,4 +1,7 @@ defmodule ElixirProvider.DataCollectorHook do + @moduledoc """ + Data collector hook + """ use GenServer require Logger @@ -25,13 +28,14 @@ defmodule ElixirProvider.DataCollectorHook do } # Starts the GenServer and initializes with options - def start_link() do + def start_link do GenServer.start_link(__MODULE__, [], name: __MODULE__) end def stop(state) do GenServer.stop(__MODULE__) collect_data(state.data_flush_interval) + %__MODULE__{ http_client: state.http_client, data_collector_endpoint: state.data_collector_endpoint, @@ -71,13 +75,15 @@ defmodule ElixirProvider.DataCollectorHook do :ok else feature_event = %FeatureEvent{ - context_kind: if(Map.get(hook_context.context, "anonymous"), do: "anonymousUser", else: "user"), + context_kind: + if(Map.get(hook_context.context, "anonymous"), do: "anonymousUser", else: "user"), creation_date: DateTime.utc_now() |> DateTime.to_unix(:millisecond), default: false, key: hook_context.flag_key, value: flag_evaluation_details.value, variation: flag_evaluation_details.variant || "SdkDefault", - user_key: Map.get(hook_context.evaluation_context, "targeting_key") || @default_targeting_key + user_key: + Map.get(hook_context.evaluation_context, "targeting_key") || @default_targeting_key } GenServer.cast(__MODULE__, {:add_event, feature_event}) @@ -89,7 +95,8 @@ defmodule ElixirProvider.DataCollectorHook do :ok else feature_event = %FeatureEvent{ - context_kind: if(Map.get(hook_context.context, "anonymous"), do: "anonymousUser", else: "user"), + context_kind: + if(Map.get(hook_context.context, "anonymous"), do: "anonymousUser", else: "user"), creation_date: DateTime.utc_now() |> DateTime.to_unix(:millisecond), default: true, key: hook_context.flag_key, @@ -120,7 +127,11 @@ defmodule ElixirProvider.DataCollectorHook do {:noreply, %{state | event_queue: []}} end - defp collect_data(%__MODULE__{event_queue: event_queue, http_client: http_client, data_collector_endpoint: endpoint}) do + defp collect_data(%__MODULE__{ + event_queue: event_queue, + http_client: http_client, + data_collector_endpoint: endpoint + }) do if Enum.empty?(event_queue) do :ok else diff --git a/openfeature/providers/elixir-provider/lib/provider/http_client.ex b/openfeature/providers/elixir-provider/lib/provider/http_client.ex index 265c7b273db..0786da26de0 100644 --- a/openfeature/providers/elixir-provider/lib/provider/http_client.ex +++ b/openfeature/providers/elixir-provider/lib/provider/http_client.ex @@ -15,11 +15,11 @@ defmodule ElixirProvider.HttpClient do } @spec start_link() :: GenServer.on_start() - def start_link() do - GenServer.start_link(__MODULE__, :ok, name: __MODULE__) + def start_link do + GenServer.start_link(__MODULE__, :ok, name: __MODULE__) end - def stop() do + def stop do GenServer.stop(__MODULE__) end diff --git a/openfeature/providers/elixir-provider/lib/provider/provider.ex b/openfeature/providers/elixir-provider/lib/provider/provider.ex index 106592af709..2eee0405177 100644 --- a/openfeature/providers/elixir-provider/lib/provider/provider.ex +++ b/openfeature/providers/elixir-provider/lib/provider/provider.ex @@ -1,16 +1,16 @@ defmodule ElixirProvider.Provider do @behaviour OpenFeature.Provider - alias OpenFeature.ResolutionDetails - alias ElixirProvider.GoFeatureFlagOptions - alias ElixirProvider.HttpClient - alias ElixirProvider.DataCollectorHook alias ElixirProvider.CacheController - alias ElixirProvider.ResponseFlagEvaluation - alias ElixirProvider.GoFWebSocketClient - alias ElixirProvider.RequestFlagEvaluation alias ElixirProvider.ContextTransformer + alias ElixirProvider.DataCollectorHook + alias ElixirProvider.GoFeatureFlagOptions alias ElixirProvider.GofEvaluationContext + alias ElixirProvider.GoFWebSocketClient + alias ElixirProvider.HttpClient + alias ElixirProvider.RequestFlagEvaluation + alias ElixirProvider.ResponseFlagEvaluation + alias OpenFeature.ResolutionDetails @moduledoc """ The GO Feature Flag provider for OpenFeature, managing HTTP requests, caching, and flag evaluation. @@ -54,7 +54,11 @@ defmodule ElixirProvider.Provider do Process.exit(ws, :normal) CacheController.clear() if(GenServer.whereis(GoFWebSocketClient), do: GoFWebSocketClient.stop()) - if(GenServer.whereis(DataCollectorHook), do: DataCollectorHook.stop(provider.data_collector_hook)) + + if(GenServer.whereis(DataCollectorHook), + do: DataCollectorHook.stop(provider.data_collector_hook) + ) + :ok end @@ -128,12 +132,13 @@ defmodule ElixirProvider.Provider do {:ok, %ResolutionDetails{value: val, reason: reason}} _ -> - {:error, {:variant_not_found, "Expected #{type} but got #{inspect(value)} for flag #{flag_key}"}} + {:error, + {:variant_not_found, + "Expected #{type} but got #{inspect(value)} for flag #{flag_key}"}} end _ -> {:error, {:flag_not_found, "Flag #{flag_key} not found"}} end end - end diff --git a/openfeature/providers/elixir-provider/lib/provider/server_supervisor.ex b/openfeature/providers/elixir-provider/lib/provider/server_supervisor.ex index 66105582c45..7d579ff653d 100644 --- a/openfeature/providers/elixir-provider/lib/provider/server_supervisor.ex +++ b/openfeature/providers/elixir-provider/lib/provider/server_supervisor.ex @@ -1,4 +1,7 @@ defmodule ElixirProvider.ServerSupervisor do + @moduledoc """ + Supervisor + """ use Supervisor def start_link(args) do diff --git a/openfeature/providers/elixir-provider/lib/provider/web_socket.ex b/openfeature/providers/elixir-provider/lib/provider/web_socket.ex index 90fafecfc5f..00fc74ea217 100644 --- a/openfeature/providers/elixir-provider/lib/provider/web_socket.ex +++ b/openfeature/providers/elixir-provider/lib/provider/web_socket.ex @@ -14,14 +14,14 @@ defmodule ElixirProvider.GoFWebSocketClient do defstruct [:conn, :websocket, :request_ref, :status, :caller, :resp_headers, :closing?] @type t :: %__MODULE__{ - conn: Mint.HTTP.t() | nil, - websocket: Mint.WebSocket.t() | nil, - request_ref: reference() | nil, - caller: {pid(), GenServer.from()} | nil, - status: integer() | nil, - resp_headers: list({String.t(), String.t()}) | nil, - closing?: boolean() - } + conn: Mint.HTTP.t() | nil, + websocket: Mint.WebSocket.t() | nil, + request_ref: reference() | nil, + caller: {pid(), GenServer.from()} | nil, + status: integer() | nil, + resp_headers: list({String.t(), String.t()}) | nil, + closing?: boolean() + } @websocket_uri "/ws/v1/flag/change" @@ -32,7 +32,7 @@ defmodule ElixirProvider.GoFWebSocketClient do end end - def stop() do + def stop do GenServer.stop(__MODULE__) end @@ -118,16 +118,16 @@ defmodule ElixirProvider.GoFWebSocketClient do {:data, ref, data} | rest ]) when websocket != nil do - case Mint.WebSocket.decode(websocket, data) do - {:ok, websocket, frames} -> - put_in(state.websocket, websocket) - |> handle_frames(frames) - |> handle_responses(rest) - - {:error, websocket, reason} -> - put_in(state.websocket, websocket) - |> reply({:error, reason}) - end + case Mint.WebSocket.decode(websocket, data) do + {:ok, websocket, frames} -> + put_in(state.websocket, websocket) + |> handle_frames(frames) + |> handle_responses(rest) + + {:error, websocket, reason} -> + put_in(state.websocket, websocket) + |> reply({:error, reason}) + end end defp handle_responses(state, [_response | rest]) do @@ -143,7 +143,6 @@ defmodule ElixirProvider.GoFWebSocketClient do %{state | closing?: true} {:text, text}, state -> - response = Jason.decode!(text) case Map.get(response, "type") do @@ -152,7 +151,8 @@ defmodule ElixirProvider.GoFWebSocketClient do CacheController.clear() Logger.info("Cache cleared due to configuration change notification.") - _ -> nil + _ -> + nil end state diff --git a/openfeature/providers/elixir-provider/mix.exs b/openfeature/providers/elixir-provider/mix.exs index ba862851eef..b44b7091c00 100644 --- a/openfeature/providers/elixir-provider/mix.exs +++ b/openfeature/providers/elixir-provider/mix.exs @@ -7,7 +7,7 @@ defmodule ElixirProvider.MixProject do version: "0.1.0", elixir: "~> 1.17", start_permanent: Mix.env() == :prod, - deps: deps(), + deps: deps() ] end @@ -24,7 +24,8 @@ defmodule ElixirProvider.MixProject do {:open_feature, git: "https://github.com/open-feature/elixir-sdk.git"}, {:jason, "~> 1.4"}, {:mint, "~> 1.6"}, - {:mint_web_socket, "~> 1.0"} + {:mint_web_socket, "~> 1.0"}, + {:credo, "~> 1.7", only: [:dev, :test], runtime: false} ] end end diff --git a/openfeature/providers/elixir-provider/mix.lock b/openfeature/providers/elixir-provider/mix.lock index 66e2e8b26ff..d145ceabf6b 100644 --- a/openfeature/providers/elixir-provider/mix.lock +++ b/openfeature/providers/elixir-provider/mix.lock @@ -1,5 +1,8 @@ %{ + "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, + "credo": {:hex, :credo, "1.7.10", "6e64fe59be8da5e30a1b96273b247b5cf1cc9e336b5fd66302a64b25749ad44d", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "71fbc9a6b8be21d993deca85bf151df023a3097b01e09a2809d460348561d8cd"}, "elixir_sdk": {:git, "https://github.com/open-feature/elixir-sdk.git", "8e08041085aedec5d661b9a9a942cdf9f2606422", []}, + "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"}, "hpax": {:hex, :hpax, "1.0.0", "28dcf54509fe2152a3d040e4e3df5b265dcb6cb532029ecbacf4ce52caea3fd2", [:mix], [], "hexpm", "7f1314731d711e2ca5fdc7fd361296593fc2542570b3105595bb0bc6d0fad601"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"}, diff --git a/openfeature/providers/elixir-provider/test/elixir_provider_test.exs b/openfeature/providers/elixir-provider/test/elixir_provider_test.exs index 35b6a5568bb..23131f98d15 100644 --- a/openfeature/providers/elixir-provider/test/elixir_provider_test.exs +++ b/openfeature/providers/elixir-provider/test/elixir_provider_test.exs @@ -1,8 +1,85 @@ defmodule ElixirProviderTest do + @moduledoc """ + Test file + """ use ExUnit.Case doctest ElixirProvider - test "greets the world" do - assert ElixirProvider.hello() == :world + ## TEST CONTEXT TRANSFORMER + + test "should use the targetingKey as user key" do + got = + ElixirProvider.ContextTransformer.transform_context(%{ + targetingKey: "user-key" + }) + + want = + {:ok, + %ElixirProvider.GofEvaluationContext{ + key: "user-key", + custom: %{} + }} + + assert got == want + end + + test "should specify the anonymous field base on the attributes" do + got = + ElixirProvider.ContextTransformer.transform_context(%{ + targetingKey: "user-key", + anonymous: true + }) + + want = + {:ok, + %ElixirProvider.GofEvaluationContext{ + key: "user-key", + custom: %{ + anonymous: true + } + }} + + assert got == want + end + + test "should fail if no targeting field is provided" do + got = + ElixirProvider.ContextTransformer.transform_context(%{ + anonymous: true, + firstname: "John", + lastname: "Doe", + email: "john.doe@gofeatureflag.org" + }) + + want = {:error, "targeting key not found"} + + assert got == want + end + + test "should fill custom fields if extra fields are present" do + got = + ElixirProvider.ContextTransformer.transform_context(%{ + targetingKey: "user-key", + anonymous: true, + firstname: "John", + lastname: "Doe", + email: "john.doe@gofeatureflag.org" + }) + + want = + {:ok, + %ElixirProvider.GofEvaluationContext{ + key: "user-key", + custom: %{ + firstname: "John", + lastname: "Doe", + email: "john.doe@gofeatureflag.org", + anonymous: true + } + }} + + assert got == want end + + ### PROVIDER TESTS end