aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Seay <sam@manuka.co>2016-09-01 09:43:23 +1200
committerGitHub <noreply@github.com>2016-09-01 09:43:23 +1200
commit3b5120b087182cb96a1a2082a5fb149276e45304 (patch)
tree53f44594fbac8a78ddf7d1fec99779ba310f4e00
parent7ff101f0fa5aaf52df07d37584472521c4b3cc55 (diff)
downloadrecaptcha-3b5120b087182cb96a1a2082a5fb149276e45304.tar.gz
recaptcha-3b5120b087182cb96a1a2082a5fb149276e45304.tar.xz
Feature/v2 rewrite (#7)
Rewrite of the API. * Change the copyright holder since its a rewrite. * Bump dependencies to their latest versions. * Revision of the README to document the new API * Break config up into keyword list (no longer a map) * A rewrite of the recaptcha verify API and move of templating into Recaptcha.Template * Add tests and credo for code style * Remove exception raising method calls (`HTTPoison.post!` and `Poison.decode!`) * Change Elixir version to 1.2 for `with` support.
-rw-r--r--LICENSE2
-rw-r--r--README.md50
-rw-r--r--config/config.exs12
-rw-r--r--config/dev.exs1
-rw-r--r--config/test.exs6
-rw-r--r--lib/recaptcha.ex86
-rw-r--r--lib/recaptcha/http.ex37
-rw-r--r--lib/recaptcha/http/mock_http_client.ex18
-rw-r--r--lib/recaptcha/response.ex8
-rw-r--r--lib/recaptcha/template.ex25
-rw-r--r--lib/template.html.eex8
-rw-r--r--mix.exs16
-rw-r--r--mix.lock14
-rw-r--r--test/recaptcha/template_test.exs25
-rw-r--r--test/recaptcha_test.exs49
-rw-r--r--test/test_helper.exs1
16 files changed, 261 insertions, 97 deletions
diff --git a/LICENSE b/LICENSE
index a492b9b..f19e5ef 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2015 Mikhail Alekseev
+Copyright (c) 2016 Samuel Seay
MIT License
diff --git a/README.md b/README.md
index 68392b5..eeb6bc7 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,19 @@
# Recaptcha
-A simple Elixir package for implementing [reCAPTCHA] in [Phoenix] applications.
+A simple Elixir package for implementing [reCAPTCHA] in [Elixir] applications.
[reCAPTCHA]: http://www.google.com/recaptcha
-[Phoenix]: http://www.phoenixframework.org/
+
+## Migration from 1.x
+
+There are several breaking changes in recaptcha version 2.
+
+The 2 most obvious are that templating is now in a separate module: `Recaptcha.Template` the `display/1` API however, has not changed. In future templating may be moved to a Phoenix specific package.
+
+The `verify` API has changed now to only accept a recaptcha response string and a set of options. (see the verify documentation for supported options). The `remote_ip` that was once passed as the first argument is now an optional argument.
+
+Most other questions about 2.x should be answered by looking over the documentation and the code. Please raise an issue
+if you have any problems with migrating.
## Installation
@@ -12,7 +22,7 @@ A simple Elixir package for implementing [reCAPTCHA] in [Phoenix] applications.
```elixir
defp deps do
[
- {:recaptcha, "~> 1.1.0"},
+ {:recaptcha, "~> 2.0"},
]
end
```
@@ -21,7 +31,7 @@ A simple Elixir package for implementing [reCAPTCHA] in [Phoenix] applications.
```elixir
def application do
- [ applications: [:phoenix, :recaptcha] ]
+ [ applications: [:recaptcha] ]
end
```
@@ -29,20 +39,19 @@ A simple Elixir package for implementing [reCAPTCHA] in [Phoenix] applications.
## Config
-Set default public and private keys in your application's config.exs
+By default the public and private keys are loaded via the `RECAPTCHA_PUBLIC_KEY` and `RECAPTCHA_PRIVATE_KEY` environment variables. Part of the reason for doing this is to encourage best practice (specifically not committing your reCAPTCHA secret key to your code base). You can of course override them in your own config any way that you like.
```elixir
config :recaptcha,
- api_config: %{ verify_url: "https://www.google.com/recaptcha/api/siteverify",
- public_key: "YOUR_PUBLIC_KEY",
- private_key: "YOUR_PRIVATE_KEY" }
+ public_key: System.get_env("RECAPTCHA_PUBLIC_KEY"),
+ secret: System.get_env("RECAPTCHA_PRIVATE_KEY")
```
## Usage
-### View
+### Render the Widget
-Use `raw` and `Recaptcha.display` methods to render the captcha
+Use `raw` (if you're using Phoenix.HTML) and `Recaptcha.Template.display/1` methods to render the captcha widget.
```html
<form name="someform" method="post" action="/somewhere">
@@ -60,36 +69,33 @@ Option | Action
`public_key` | Sets key to the `data-sitekey` reCaptcha div attribute | Public key from the config file
+### Verify API
-### Controller
-
-Recaptcha provides `verify` method, that can be used like this:
+Recaptcha provides the `verify/2` method, that can be used like this:
```elixir
def create(conn, params) do
# some code
- case Recaptcha.verify(conn.remote_ip, params["g-recaptcha-response"]) do
- :ok -> do_something
- :error -> handle_error
+ case Recaptcha.verify(params["g-recaptcha-response"]) do
+ {:ok, response} -> do_something
+ {:error, errors} -> handle_error
end
end
```
`verify` method sends a `POST` request to the reCAPTCHA API and returns 2 possible values:
-`:ok` -> The captcha is valid
-
-`:error` -> Server returned `missing-input-response` or `invalid-input-response` error codes
-
-If the server returns `missing-input-secret` or `invalid-input-secret`, `RuntimeError` is raised
+`{:ok, %Recaptcha.Response{challenge_ts: timestamp, hostname: host}}` -> The captcha is valid, see the [documentation](https://developers.google.com/recaptcha/docs/verify#api-response) for more details.
+`{:error, errors}` -> `errors` contains atomised versions of the errors returned by the API, See the [error documentation](https://developers.google.com/recaptcha/docs/verify#error-code-reference) for more details. Errors caused by timeouts in HTTPoison or Poison encoding are also returned as atoms.
`verify` method also accepts a keyword list as the third parameter with the following options:
Option | Action | Default
:---------------------- | :----------------------------------------------------- | :------------------------
`timeout` | Time to wait before timeout | 5000 (ms)
-`private_key` | Private key to send as a parameter of the API request | Private key from the config file
+`secret` | Private key to send as a parameter of the API request | Private key from the config file
+`remote_ip` | Optional. The user's IP address, used by reCaptcha | no default
## Contributing
diff --git a/config/config.exs b/config/config.exs
index a87116e..3c34501 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -1,7 +1,9 @@
use Mix.Config
-config :recaptcha, :api_config,
- %{ verify_url: "https://www.google.com/recaptcha/api/siteverify",
- public_key: "YOUR_PUBLIC_KEY",
- private_key: "YOUR_PRIVATE_KEY"
- }
+config :recaptcha,
+ verify_url: "https://www.google.com/recaptcha/api/siteverify",
+ timeout: 5000,
+ public_key: System.get_env("RECAPTCHA_PUBLIC_KEY"),
+ secret: System.get_env("RECAPTCHA_PRIVATE_KEY")
+
+import_config "#{Mix.env}.exs"
diff --git a/config/dev.exs b/config/dev.exs
new file mode 100644
index 0000000..d2d855e
--- /dev/null
+++ b/config/dev.exs
@@ -0,0 +1 @@
+use Mix.Config
diff --git a/config/test.exs b/config/test.exs
new file mode 100644
index 0000000..18aed6e
--- /dev/null
+++ b/config/test.exs
@@ -0,0 +1,6 @@
+use Mix.Config
+
+config :recaptcha,
+ http_client: Recaptcha.Http.MockClient,
+ secret: "test_secret",
+ public_key: "test_public_key"
diff --git a/lib/recaptcha.ex b/lib/recaptcha.ex
index e0656c3..0037480 100644
--- a/lib/recaptcha.ex
+++ b/lib/recaptcha.ex
@@ -1,75 +1,53 @@
defmodule Recaptcha do
- require Elixir.EEx
+ @moduledoc """
+ A module for verifying reCAPTCHA version 2.0 response strings.
- @secret_key_errors ~w(missing-input-secret invalid-input-secret)
-
- EEx.function_from_file :defp, :render_template, "lib/template.html.eex", [:assigns]
-
- @doc """
- Returns a string with reCAPTCHA code
-
- To convert the string to html code, use Phoenix.HTML.Raw/1 method
+ See the [documentation](https://developers.google.com/recaptcha/docs/verify) for more details.
"""
- def display(options \\ []) do
- public_key = options[:public_key] || config.public_key
- render_template(public_key: public_key, options: options)
- end
+ @secret Application.get_env(:recaptcha, :secret)
+ @http_client Application.get_env(:recaptcha, :http_client, Recaptcha.Http)
@doc """
- Verifies reCAPTCHA response string.
+ Verifies a reCAPTCHA response string.
- The function returns :ok or :error, depending on the verification's result
+ ## Options
- :ok is returned when the response code was successfully verified
+ * `:timeout` - the timeout for the request (defaults to 5000ms)
+ * `:secret` - the secret key used by recaptcha (defaults to the secret provided in application config)
+ * `:remote_ip` - the IP address of the user (optional and not set by default)
- :error is returned when the response is nil or if the reCAPTCHA server returned
- a missing-input-response or invalid-input-response code
+ ## Example
- The function raises RuntimeError if the server returned a missing-input-secret or invalid-input-secret
- code
+ {:ok, api_response} = Recaptcha.verify("response_string")
"""
- def verify(remote_ip, response, options \\ [])
+ @spec verify(String.t, [timeout: integer, secret: String.t, remote_ip: String.t]) :: {:ok, Recaptcha.Response.t} | {:error, [atom]}
+ def verify(response, options \\ [])
- def verify(remote_ip, response, options) when is_tuple(remote_ip) do
- verify(:inet_parse.ntoa(remote_ip), response, options)
+ def verify(nil = _response, _) do
+ {:error, [:invalid_input_response]}
end
- def verify(_remote_ip, nil = _response, _options), do: :error
-
- def verify(remote_ip, response, options) do
- case api_response(remote_ip, response, options) do
- %{"success" => true} ->
- :ok
- %{"success" => false, "error-codes" => error_codes} ->
- handle_error_codes(error_codes)
- %{"success" => false} ->
- :error
+ def verify(response, options) do
+ case @http_client.request_verification(request_body(response, options), Keyword.take(options, [:timeout])) do
+ {:error, errors} -> {:error, errors}
+ {:ok, %{"success" => false, "error-codes" => errors}} -> {:error, Enum.map(errors, fn(error) -> atomise_api_error(error) end) }
+ {:ok, %{"success" => true, "challenge_ts" => timestamp, "hostname" => host}} -> {:ok, %Recaptcha.Response{challenge_ts: timestamp, hostname: host}}
end
end
- defp api_response(remote_ip, response, options) do
- private_key = options[:private_key] || config.private_key
- timeout = options[:timeout] || 5000
- body_content = URI.encode_query(%{"remoteip" => to_string(remote_ip),
- "response" => response,
- "secret" => private_key})
- headers = [{"Content-type", "application/x-www-form-urlencoded"}, {"Accept", "application/json"}]
+ defp request_body(response, options) do
+ body_options = Keyword.take(options, [:remote_ip, :secret])
+ application_options = [secret: @secret]
- HTTPoison.post!(config.verify_url, body_content, headers, timeout: timeout).body
- |> Poison.decode!
+ # override application secret with options secret if it exists
+ application_options
+ |> Keyword.merge(body_options)
+ |> Keyword.put(:response, response)
+ |> URI.encode_query
end
- defp config do
- Application.get_env(:recaptcha, :api_config)
+ defp atomise_api_error(error) do
+ String.replace(error, "-", "_")
+ |> String.to_atom
end
-
- defp handle_error_codes(error_codes) do
- if Enum.any?(error_codes, fn(code) -> Enum.member?(@secret_key_errors, code) end) do
- raise RuntimeError,
- message: "reCaptcha API has declined the private key. Please make sure you've set the correct private key"
- else
- :error
- end
- end
-
end
diff --git a/lib/recaptcha/http.ex b/lib/recaptcha/http.ex
new file mode 100644
index 0000000..01c088a
--- /dev/null
+++ b/lib/recaptcha/http.ex
@@ -0,0 +1,37 @@
+defmodule Recaptcha.Http do
+ @moduledoc """
+ Responsible for managing HTTP requests to the reCAPTCHA API
+ """
+ @headers [{"Content-type", "application/x-www-form-urlencoded"}, {"Accept", "application/json"}]
+ @url Application.get_env(:recaptcha, :verify_url)
+ @timeout Application.get_env(:recaptcha, :timeout, 5000)
+
+ @doc """
+ Sends an HTTP request to the reCAPTCHA version 2.0 API.
+
+ See the [documentation](https://developers.google.com/recaptcha/docs/verify#api-response) for more details on the API response.
+
+ ## Options
+
+ * `:timeout` - the timeout for the request (defaults to 5000ms)
+
+ ## Example
+
+ {:ok, %{ "success" => success, "challenge_ts" => ts, "hostname" => host, "error-codes" => errors}} = Recaptcha.Http.request_verification(%{ secret: "secret", response: "response", remote_ip: "remote_ip"})
+ """
+ @spec request_verification(map, [timeout: integer]) :: {:ok, map} | {:error, [atom]}
+ def request_verification(body, options \\ []) do
+ result =
+ with {:ok, response} <- HTTPoison.post(@url, body, @headers, timeout: options[:timeout] || @timeout),
+ {:ok, data} <- Poison.decode(response.body) do
+ {:ok, data}
+ end
+
+ case result do
+ {:ok, data} -> {:ok, data}
+ {:error, :invalid} -> {:error, [:invalid_api_response]}
+ {:error, {:invalid, _reason}} -> {:error, [:invalid_api_response]}
+ {:error, %{reason: reason}} -> {:error, [reason]}
+ end
+ end
+end
diff --git a/lib/recaptcha/http/mock_http_client.ex b/lib/recaptcha/http/mock_http_client.ex
new file mode 100644
index 0000000..2355dd3
--- /dev/null
+++ b/lib/recaptcha/http/mock_http_client.ex
@@ -0,0 +1,18 @@
+defmodule Recaptcha.Http.MockClient do
+ @moduledoc """
+ A mock HTTP client used for testing.
+ """
+
+ def request_verification(body, options \\ [])
+
+ def request_verification("response=valid_response&secret=6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe" = body, options) do
+ send self(), {:request_verification, body, options}
+ {:ok, %{"success" => true, "challenge_ts" => "timestamp", "hostname" => "localhost"}}
+ end
+
+ # every other match is a pass through to the real client
+ def request_verification(body, options) do
+ send self(), {:request_verification, body, options}
+ Recaptcha.Http.request_verification(body, options)
+ end
+end
diff --git a/lib/recaptcha/response.ex b/lib/recaptcha/response.ex
new file mode 100644
index 0000000..f8adb18
--- /dev/null
+++ b/lib/recaptcha/response.ex
@@ -0,0 +1,8 @@
+defmodule Recaptcha.Response do
+ @moduledoc """
+ A struct representing the successful recaptcha response from the reCAPTCHA API.
+ """
+ defstruct challenge_ts: "", hostname: ""
+
+ @type t :: %__MODULE__{challenge_ts: String.t, hostname: String.t}
+end
diff --git a/lib/recaptcha/template.ex b/lib/recaptcha/template.ex
new file mode 100644
index 0000000..d644266
--- /dev/null
+++ b/lib/recaptcha/template.ex
@@ -0,0 +1,25 @@
+defmodule Recaptcha.Template do
+ @moduledoc """
+ Responsible for rendering boilerplate recaptcha HTML code, supports noscript fallback.
+
+ Currently the [explicit render](https://developers.google.com/recaptcha/docs/display#explicit_render) functionality
+ is not supported.
+
+ In future this module may be separated out into a Phoenix specific library.
+ """
+ require Elixir.EEx
+
+ EEx.function_from_file :defp, :render_template, "lib/template.html.eex", [:assigns]
+
+ @public_key Application.get_env(:recaptcha, :public_key)
+
+ @doc """
+ Returns a string with reCAPTCHA code
+
+ To convert the string to html code, use Phoenix.HTML.Raw/1 method
+ """
+ def display(options \\ []) do
+ public_key = options[:public_key] || @public_key
+ render_template(public_key: public_key, options: options)
+ end
+end
diff --git a/lib/template.html.eex b/lib/template.html.eex
index c9c5385..5f9109c 100644
--- a/lib/template.html.eex
+++ b/lib/template.html.eex
@@ -2,10 +2,10 @@
<div class="g-recaptcha"
data-sitekey="<%= @public_key %>"
- data-theme="<%= @options[:theme]%>"
- data-type="<%= @options[:type]%>"
- data-tabindex="<%= @options[:tabindex]%>"
- data-size="<%= @options[:size]%>">
+ data-theme="<%= @options[:theme] %>"
+ data-type="<%= @options[:type] %>"
+ data-tabindex="<%= @options[:tabindex] %>"
+ data-size="<%= @options[:size] %>">
</div>
<%= if @options[:noscript] do %>
diff --git a/mix.exs b/mix.exs
index bf4dc31..46c88c1 100644
--- a/mix.exs
+++ b/mix.exs
@@ -3,8 +3,8 @@ defmodule Recaptcha.Mixfile do
def project do
[app: :recaptcha,
- version: "1.1.1",
- elixir: "~> 1.0",
+ version: "2.0.0",
+ elixir: "~> 1.2",
description: description,
deps: deps,
package: package]
@@ -16,21 +16,23 @@ defmodule Recaptcha.Mixfile do
defp description do
"""
- A simple reCaptcha package for Phoenix applications.
+ A simple reCaptcha package for Elixir applications, provides verification
+ and templates for rendering forms with the reCaptcha widget
"""
end
defp deps do
[
- {:httpoison, "~> 0.7"},
- {:poison, "~> 1.5"}
+ {:httpoison, "~> 0.9.0"},
+ {:poison, "~> 2.0"},
+ {:credo, "~> 0.4", only: [:dev, :test]}
]
end
defp package do
[files: ["lib", "mix.exs", "README.md", "LICENSE"],
- maintainers: ["Alekseev Mikhail"],
+ maintainers: ["Samuel Seay"],
licenses: ["MIT"],
- links: %{"GitHub" => "https://github.com/JustMikey/recaptcha"}]
+ links: %{"GitHub" => "https://github.com/samueljseay/recaptcha"}]
end
end
diff --git a/mix.lock b/mix.lock
index eac4b1b..668749b 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,7 +1,13 @@
-%{"hackney": {:hex, :hackney, "1.3.2"},
- "httpoison": {:hex, :httpoison, "0.7.4"},
+%{"bunt": {:hex, :bunt, "0.1.6", "5d95a6882f73f3b9969fdfd1953798046664e6f77ec4e486e6fafc7caad97c6f", [:mix], []},
+ "certifi": {:hex, :certifi, "0.4.0", "a7966efb868b179023618d29a407548f70c52466bf1849b9e8ebd0e34b7ea11f", [:rebar3], []},
+ "credo": {:hex, :credo, "0.4.11", "03a64e9d53309b7132556284dda0be57ba1013885725124cfea7748d740c6170", [:mix], [{:bunt, "~> 0.1.6", [hex: :bunt, optional: false]}]},
+ "hackney": {:hex, :hackney, "1.6.1", "ddd22d42db2b50e6a155439c8811b8f6df61a4395de10509714ad2751c6da817", [:rebar3], [{:certifi, "0.4.0", [hex: :certifi, optional: false]}, {:idna, "1.2.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.0", [hex: :ssl_verify_fun, optional: false]}]},
+ "httpoison": {:hex, :httpoison, "0.9.1", "6c2b4eaf2588a6f3ef29663d28c992531ca3f0bc832a97e0359bc822978e1c5d", [:mix], [{:hackney, "~> 1.6.0", [hex: :hackney, optional: false]}]},
"httpotion": {:hex, :httpotion, "2.1.0"},
"ibrowse": {:git, "https://github.com/cmullaparthi/ibrowse.git", "ea3305d21f37eced4fac290f64b068e56df7de80", [tag: "v4.1.2"]},
- "idna": {:hex, :idna, "1.0.2"},
- "poison": {:hex, :poison, "1.5.0"},
+ "idna": {:hex, :idna, "1.2.0", "ac62ee99da068f43c50dc69acf700e03a62a348360126260e87f2b54eced86b2", [:rebar3], []},
+ "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []},
+ "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []},
+ "poison": {:hex, :poison, "2.2.0", "4763b69a8a77bd77d26f477d196428b741261a761257ff1cf92753a0d4d24a63", [:mix], []},
+ "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.0", "edee20847c42e379bf91261db474ffbe373f8acb56e9079acb6038d4e0bf414f", [:rebar, :make], []},
"ssl_verify_hostname": {:hex, :ssl_verify_hostname, "1.0.5"}}
diff --git a/test/recaptcha/template_test.exs b/test/recaptcha/template_test.exs
new file mode 100644
index 0000000..335eb5f
--- /dev/null
+++ b/test/recaptcha/template_test.exs
@@ -0,0 +1,25 @@
+defmodule RecaptchaTemplateTest do
+ use ExUnit.Case, async: true
+
+ test "supplying options to display/1 renders them in the g-recaptcha div" do
+ template_string = Recaptcha.Template.display(theme: "dark", type: "audio", tabindex: 1, size: "compact")
+
+ assert template_string =~ "data-theme=\"dark\""
+ assert template_string =~ "data-type=\"audio\""
+ assert template_string =~ "data-tabindex=\"1\""
+ assert template_string =~ "data-size=\"compact\""
+ end
+
+ test "supplying a public key in options to display/1 overrides it in the g-recaptcha-div" do
+ template_string = Recaptcha.Template.display(public_key: "override_test_public_key")
+
+ assert template_string =~ "data-sitekey=\"override_test_public_key\""
+ end
+
+ test "supplying noscript option displays the noscript fallback" do
+ template_string = Recaptcha.Template.display(noscript: true)
+
+ assert template_string =~ "<noscript>"
+ assert template_string =~ "https://www.google.com/recaptcha/api/fallback?k=test_public_key"
+ end
+end
diff --git a/test/recaptcha_test.exs b/test/recaptcha_test.exs
new file mode 100644
index 0000000..ad57772
--- /dev/null
+++ b/test/recaptcha_test.exs
@@ -0,0 +1,49 @@
+defmodule RecaptchaTest do
+ use ExUnit.Case, async: true
+
+ # see https://developers.google.com/recaptcha/docs/faq#id-like-to-run-automated-tests-with-recaptcha-v2-what-should-i-do
+ @google_test_secret "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe"
+
+ test "When the supplied g-recaptcha-response is nil, :invalid_input_response is returned" do
+ assert {:error, messages} = Recaptcha.verify(nil)
+ assert messages == [:invalid_input_response]
+ end
+
+ test "When the supplied g-recaptcha-response is not nil, but invalid, multiple errors are returned" do
+ assert {:error, messages} = Recaptcha.verify("not_valid")
+ assert messages == [:invalid_input_response, :invalid_input_secret]
+ end
+
+ test "When the correct secret is supplied but g-recaptcha-response is invalid :invalid_input_response' is returned" do
+ assert {:error, messages} = Recaptcha.verify("not_valid", secret: @google_test_secret)
+ assert messages == [:invalid_input_response]
+ end
+
+ test "When a valid response is supplied, a success response is returned" do
+ assert {:ok, %{challenge_ts: _, hostname: _}} = Recaptcha.verify("valid_response", secret: "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe")
+ end
+
+ test "When secret is not overridden the configured secret is used" do
+ Recaptcha.verify("valid_response")
+
+ assert_received {:request_verification, "response=valid_response&secret=test_secret", _}
+ end
+
+ test "When the timeout is overridden that config is passed to verify/2 as an option" do
+ Recaptcha.verify("valid_response", timeout: 25000)
+
+ assert_received {:request_verification, _, [timeout: 25000]}
+ end
+
+ test "Remote IP is used in the request body when it is passed into verify/2 as an option" do
+ Recaptcha.verify("valid_response", remote_ip: "192.168.1.1")
+
+ assert_received {:request_verification, "response=valid_response&secret=test_secret&remote_ip=192.168.1.1", _}
+ end
+
+ test "Adding unsupported options does not append them to the request body" do
+ Recaptcha.verify("valid_response", unsupported_option: "not_valid")
+
+ assert_received {:request_verification, "response=valid_response&secret=test_secret", _}
+ end
+end
diff --git a/test/test_helper.exs b/test/test_helper.exs
new file mode 100644
index 0000000..869559e
--- /dev/null
+++ b/test/test_helper.exs
@@ -0,0 +1 @@
+ExUnit.start()