diff --git a/README.md b/README.md index ce98c205..8f4f4a82 100644 --- a/README.md +++ b/README.md @@ -122,8 +122,11 @@ We wanted to "supervise" our payments, and power utilities to process recurring payments, subscriptions with it. But yes, as of now, it is a bottle neck and unnecessary. -It's slated to be removed in [`v2.0.0`][milestone-2_0_0_alpha] and any supervised / async / -parallel work can be explicitly managed via native elixir constructs. +It's slated to be removed in [`v2.0.0`][milestone-2_0_0_alpha] and any +supervised/async/parallel work can be explicitly managed via native elixir +constructs. + +**In fact, it's already been removed from our [dev](#) branch.** [milestone-2_0_0_alpha]: https://github.com/aviabird/gringotts/milestone/3 [reason]: http://harrypotter.wikia.com/wiki/Gringotts diff --git a/lib/gringotts.ex b/lib/gringotts.ex index 003c6aba..161d1ae8 100644 --- a/lib/gringotts.ex +++ b/lib/gringotts.ex @@ -295,7 +295,6 @@ defmodule Gringotts do end defp get_and_validate_config(gateway) do - # Keep the key name and adapter the same in the config in application config = Application.get_env(:gringotts, gateway) # The following call to validate_config might raise an error gateway.validate_config(config) diff --git a/lib/gringotts/adapter.ex b/lib/gringotts/adapter.ex index 5edd06f2..f4380b87 100644 --- a/lib/gringotts/adapter.ex +++ b/lib/gringotts/adapter.ex @@ -49,7 +49,7 @@ defmodule Gringotts.Adapter do Raises a run-time `ArgumentError` if any of the `required_config` values is not available or missing from the Application config. """ - def validate_config(config) do + def validate_config(config) when is_list(config) do missing_keys = Enum.reduce(@required_config, [], fn key, missing_keys -> if config[key] in [nil, ""], do: [key | missing_keys], else: missing_keys @@ -58,6 +58,12 @@ defmodule Gringotts.Adapter do raise_on_missing_config(missing_keys, config) end + def validate_config(config) when is_map(config) do + config + |> Enum.into([]) + |> validate_config + end + defp raise_on_missing_config([], _config), do: :ok defp raise_on_missing_config(key, config) do diff --git a/templates/gateway.eex b/templates/gateway.eex index 6169bae4..48de7269 100644 --- a/templates/gateway.eex +++ b/templates/gateway.eex @@ -65,7 +65,7 @@ defmodule Gringotts.Gateways.<%= gateway_module %> do ## Following the examples - 1. First, set up a sample application and configure it to work with MONEI. + 1. First, set up a sample application and configure it to work with <%= gateway %>. - You could do that from scratch by following our [Getting Started][gs] guide. - To save you time, we recommend [cloning our example repo][example] that gives you a pre-configured sample app ready-to-go. @@ -258,7 +258,6 @@ defmodule Gringotts.Gateways.<%= gateway_module %> do # For consistency with other gateway implementations, make your (final) # network request in here, and parse it using another private method called # `respond`. - @spec commit(_) :: {:ok | :error, Response} defp commit(_) do # resp = HTTPoison.request(args, ...) # respond(resp, ...) @@ -266,7 +265,6 @@ defmodule Gringotts.Gateways.<%= gateway_module %> do # Parses <%= gateway %>'s response and returns a `Gringotts.Response` struct # in a `:ok`, `:error` tuple. - @spec respond(term) :: {:ok | :error, Response} defp respond(<%= gateway_underscore %>_response) defp respond({:ok, %{status_code: 200, body: body}}), do: "something" defp respond({:ok, %{status_code: status_code, body: body}}), do: "something" diff --git a/templates/integration.eex b/templates/integration.eex index 74bc5fc7..46e5b063 100644 --- a/templates/integration.eex +++ b/templates/integration.eex @@ -1,7 +1,10 @@ -defmodule Gringotts.Integration.Gateways.<%= gateway_module <> "Test"%> do - # Integration tests for the <%= gateway_module%> +defmodule Gringotts.Integration.Gateways.<%= gateway_module <> "Test" %> do + # Integration tests for the <%= gateway_module %> + # + # Note that your tests SHOULD NOT directly call the <%= gateway_module %>, but + # all calls must be via Gringotts' public API as defined in `lib`gringotts.ex` - use ExUnit.Case, async: false + use ExUnit.Case, async: true alias Gringotts.Gateways.<%= gateway_module%> @moduletag :integration @@ -15,6 +18,7 @@ defmodule Gringotts.Integration.Gateways.<%= gateway_module <> "Test"%> do end # Group the test cases by public api + describe "purchase" do end diff --git a/templates/test.eex b/templates/test.eex index 93c0b62d..f91b8d47 100644 --- a/templates/test.eex +++ b/templates/test.eex @@ -1,32 +1,64 @@ defmodule Gringotts.Gateways.<%= gateway_module <> "Test" %> do - # The file contains mocked tests for <%= gateway_module%> + # The file contains mock tests for <%= gateway_module%> - # We recommend using [mock][1] for this, you can place the mock responses from - # the Gateway in `test/mocks/<%= mock_response_filename %>` file, which has also been - # generated for you. - # - # [1]: https://github.com/jjh42/mock + # We recommend using [`Bypass`][bypass] for this as it allows us to inspect + # the request body that is sent to the gateway. + + # After all, the only thing Gringotts does, is building HTTPoison requests + # from arguments. Thus by validating that a request has been properly + # constructed from the given arguments we accurately cover the behaviour of + # the module. + + # For inspiration and guidance to writing mock tests, please refer the mock + # tests of the MONEI gateway. Bypass has excellent documentation and there are + # numerous blog posts detailing good practices. - # Load the mock response file before running the tests. - Code.require_file "../mocks/<%= mock_response_filename %>", __DIR__ + # [bypass]: https://github.com/pspdfkit-labs/bypass - use ExUnit.Case, async: false + use ExUnit.Case, async: true + + import Bypass + alias Gringotts.Gateways.<%= gateway_module%> - import Mock + alias Plug.{Conn, Parsers} - # Group the test cases by public api - describe "purchase" do + # A new Bypass instance is needed per test, so that we can do parallel tests + setup do + bypass = Bypass.open() + {:ok, bypass: bypass} end - describe "authorize" do - end + @doc """ + Parses the body of the `Plug.Conn.t`. - describe "capture" do - end + This is very useful when testing with `Bypass` to parse body of the request + built in the test. This makes it dead-simple to write asserts on the request + body! + + ## Example + ``` + test "something", %{bypass: bypass} do + Bypass.expect(bypass, "POST", "some/endpoint/", fn conn -> + p_conn = parse(conn) + params = p_conn.body_params + assert params["amount"] == "42.00" + assert params["currency"] == "USD" + Conn.resp(conn, 200, "the_mocked_reponse_body") + end) - describe "void" do + {:ok, response} = Gateway.authorize(@amount42, @card, @opts) + assert "something about the mocked response if necessary" end + ``` + """ + @spec parse(Plug.Conn.t(), keyword) :: Plug.Conn.t() + def parse(conn, opts \\ []) do + opts = Keyword.put_new(opts, :parsers, [Parsers.URLENCODED]) - describe "refund" do + # if your gateway returns JSON instead of URL Encoded responses, use the + # JSON parser + + # opts = Keyword.put_new(opts, :parsers, [Parsers.JSON]) + Parsers.call(conn, Parsers.init(opts)) end end