Repository: dashbitco/nimble_options
Pull request #50 (2020-09-24 14:54:34)

Validate default values
Currently, default values are not validated so if the user changes the schema and forgets to update related default values accordingly, the resulting options returned by `validate` will contain invalid options. This PR fixes the issue and also allows addressing #49 more consistently by setting `[]` as a default value.


Patch:
diff --git a/lib/nimble_options.ex b/lib/nimble_options.ex
index 9611023..3886e1e 100644
--- a/lib/nimble_options.ex
+++ b/lib/nimble_options.ex
@@ -305,7 +305,8 @@ defmodule NimbleOptions do
 
       :no_value ->
         if Keyword.has_key?(schema_opts, :default) do
-          {:cont, Keyword.put(opts, key, schema_opts[:default])}
+          opts_with_default = Keyword.put(opts, key, schema_opts[:default])
+          reduce_options({key, schema_opts}, opts_with_default)
         else
           {:cont, opts}
         end


Test patch:
diff --git a/test/nimble_options_test.exs b/test/nimble_options_test.exs
index 32f557f..2ed6a06 100644
--- a/test/nimble_options_test.exs
+++ b/test/nimble_options_test.exs
@@ -86,6 +86,30 @@ defmodule NimbleOptionsTest do
       schema = [context: [default: :ok]]
       assert NimbleOptions.validate([context: :given], schema) == {:ok, [context: :given]}
     end
+
+    test "is validated" do
+      schema = [
+        processors: [
+          type: :keyword_list,
+          default: [],
+          keys: [
+            stages: [type: :integer, default: "10"]
+          ]
+        ]
+      ]
+
+      opts = [processors: []]
+
+      assert NimbleOptions.validate(opts, schema) == {
+               :error,
+               %ValidationError{
+                 key: :stages,
+                 keys_path: [:processors],
+                 message: "expected :stages to be an integer, got: \"10\"",
+                 value: "10"
+               }
+             }
+    end
   end
 
   describe "required options" do
@@ -122,11 +146,6 @@ defmodule NimbleOptionsTest do
       schema = [context: [rename_to: :new_context], new_context: []]
       assert NimbleOptions.validate([], schema) == {:ok, []}
     end
-
-    test "is ignored with default" do
-      schema = [context: [rename_to: :new_context, default: 1], new_context: []]
-      assert NimbleOptions.validate([], schema) == {:ok, [context: 1]}
-    end
   end
 
   describe "doc" do
@@ -161,10 +180,12 @@ defmodule NimbleOptionsTest do
       assert NimbleOptions.validate([], schema) == {:ok, []}
     end
 
-    test "does not warn when using default" do
+    test "warns when using default" do
       schema = [context: [deprecated: "Use something else", default: :ok]]
 
-      assert NimbleOptions.validate([], schema) == {:ok, [context: :ok]}
+      assert capture_io(:stderr, fn ->
+               assert NimbleOptions.validate([], schema) == {:ok, [context: :ok]}
+             end) =~ ":context is deprecated. Use something else"
     end
   end
 
@@ -636,6 +657,20 @@ defmodule NimbleOptionsTest do
       assert NimbleOptions.validate(opts, schema) == {:ok, [processors: [stages: 10]]}
     end
 
+    test "empty default option with default values" do
+      schema = [
+        processors: [
+          type: :keyword_list,
+          default: [],
+          keys: [
+            stages: [default: 10]
+          ]
+        ]
+      ]
+
+      assert NimbleOptions.validate([], schema) == {:ok, [processors: [stages: 10]]}
+    end
+
     test "all required options present" do
       schema = [
         processors: [