Skip to content

Commit

Permalink
Add integration test against mocked HTTP client.
Browse files Browse the repository at this point in the history
This is overkill for now, but will become necessary to prove we've got
everything wired up properly to forward trace context in HTTP headers.
  • Loading branch information
Garth Kidd authored and garthk committed May 23, 2020
1 parent 6af40cb commit b18a0ae
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 28 deletions.
2 changes: 2 additions & 0 deletions plug_gateway/config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ config :plug_gateway,
api_endpoint: System.get_env("BACKEND_API_URL"),
auth_token: System.get_env("BACKEND_AUTH_TOKEN"),
port: String.to_integer(System.get_env("PORT", "4000"))

if Mix.env() in [:test], do: import_config("#{Mix.env()}.exs")
6 changes: 6 additions & 0 deletions plug_gateway/config/test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use Mix.Config

config :plug_gateway,
api_endpoint: "http://api.example.com/api",
auth_token: "BACKEND_AUTH_TOKEN",
http_module: PlugGateway.HTTPMock
18 changes: 8 additions & 10 deletions plug_gateway/lib/plug_gateway/backend_client.ex
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
defmodule PlugGateway.BackendClient do
@moduledoc """
PlugGateway back end client.
"""

def get(url, opts \\ []) do
auth_token = PlugGateway.Config.get().auth_token
headers = [{"authorization", "Bearer #{auth_token}"}]

url
|> HTTPoison.get(headers)
|> case do
{:ok, %HTTPoison.Response{status_code: status_code, body: body}} -> {:ok, status_code, body}
{:error, reason} -> {:error, reason}
end
def get(path) do
config = PlugGateway.Config.get()
url = config.api_endpoint <> path
headers = [{"authorization", "Bearer #{config.auth_token}"}]
config.http_module.get(url, headers)
end
end
2 changes: 1 addition & 1 deletion plug_gateway/lib/plug_gateway/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule PlugGateway.Config do
PlugGateway configuration.
"""

defstruct [:api_endpoint, :auth_token, :port]
defstruct [:api_endpoint, :auth_token, http_module: PlugGateway.HTTP.API.Poisonous, port: 4000]

@doc "Get our configuration."
def get do
Expand Down
29 changes: 29 additions & 0 deletions plug_gateway/lib/plug_gateway/http_api.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
defmodule PlugGateway.HTTP.API do
@moduledoc """
PlugGateway API for speaking to HTTP back ends.
"""

@doc "Get a URL."
@callback get(
url :: String.t(),
headers: [{String.t(), iodata()}]
) :: {:ok, integer(), String.t()} | {:error, term()}
end

defmodule PlugGateway.HTTP.API.Poisonous do
@moduledoc """
PlugGateway implementation for speaking to HTTP back ends.
Adapts `c:PlugGateway.HTTP.API.get/1` to `HTTPoison.get/2`.
"""

@behaviour PlugGateway.HTTP.API

@impl true
def get(url, headers) do
case HTTPoison.get(url, headers) do
{:ok, %HTTPoison.Response{status_code: status_code, body: body}} -> {:ok, status_code, body}
{:error, reason} -> {:error, reason}
end
end
end
32 changes: 15 additions & 17 deletions plug_gateway/lib/plug_gateway/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ defmodule PlugGateway.Router do

alias PlugGateway.BackendClient

plug :match
plug Plug.Parsers, parsers: [:urlencoded, :json],
plug(:match)

plug(Plug.Parsers,
parsers: [:urlencoded, :json],
pass: ["*/*"],
json_decoder: Jason
plug :dispatch
)

plug(:dispatch)

get "/" do
send_resp(conn, 200, "Hello World")
Expand All @@ -22,26 +26,20 @@ defmodule PlugGateway.Router do
end

get "/users" do
response =
BackendClient.get(backend_api_endpoint() <> "/users")
|> case do
{:ok, status_code, body} -> send_resp(conn, status_code, body)
{:error, reason} -> send_resp(conn, 502, ~s|{"errors":"#{inspect reason}"}|)
end
case BackendClient.get("/users") do
{:ok, status_code, body} -> send_resp(conn, status_code, body)
{:error, reason} -> send_resp(conn, 502, ~s|{"errors":"#{inspect(reason)}"}|)
end
end

get "/users_n_plus_1" do
response =
BackendClient.get(backend_api_endpoint() <> "/users_n_plus_1")
|> case do
{:ok, status_code, body} -> send_resp(conn, status_code, body)
{:error, reason} -> send_resp(conn, 502, ~s|{"errors":"#{inspect reason}"}|)
end
case BackendClient.get("/users_n_plus_1") do
{:ok, status_code, body} -> send_resp(conn, status_code, body)
{:error, reason} -> send_resp(conn, 502, ~s|{"errors":"#{inspect(reason)}"}|)
end
end

match _ do
send_resp(conn, 404, "Not Found")
end

defp backend_api_endpoint, do: PlugGateway.Config.get().api_endpoint
end
1 change: 1 addition & 0 deletions plug_gateway/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ defmodule PlugGateway.MixProject do
[
{:httpoison, "~> 1.6"},
{:jason, "~> 1.1"},
{:mox, "~> 0.5.2", only: :test},
{:plug_cowboy, "~> 2.1"}
]
end
Expand Down
1 change: 1 addition & 0 deletions plug_gateway/mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"mox": {:hex, :mox, "0.5.2", "55a0a5ba9ccc671518d068c8dddd20eeb436909ea79d1799e2209df7eaa98b6c", [:mix], [], "hexpm", "df4310628cd628ee181df93f50ddfd07be3e5ecc30232d3b6aadf30bdfe6092b"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
"plug": {:hex, :plug, "1.8.3", "12d5f9796dc72e8ac9614e94bda5e51c4c028d0d428e9297650d09e15a684478", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "164baaeb382d19beee0ec484492aa82a9c8685770aee33b24ec727a0971b34d0"},
"plug_cowboy": {:hex, :plug_cowboy, "2.1.0", "b75768153c3a8a9e8039d4b25bb9b14efbc58e9c4a6e6a270abff1cd30cbe320", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "6cd8ddd1bd1fbfa54d3fc61d4719c2057dae67615395d58d40437a919a46f132"},
Expand Down
43 changes: 43 additions & 0 deletions plug_gateway/test/plug_gateway_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ defmodule PlugGatewayTest do

alias PlugGateway.Router

import Mox, only: [set_mox_from_context: 1, verify_on_exit!: 1]

@router_opts Router.init([])

test "root endpoint" do
Expand All @@ -29,6 +31,47 @@ defmodule PlugGatewayTest do
assert conn.resp_body == "Not Found"
end

test "users route to back end", ctx do
assert PlugGateway.Config.get().http_module == PlugGateway.HTTPMock
set_mox_from_context(ctx)
verify_on_exit!(ctx)

test_pid = self()

# Expect a call to PlugGateway.HTTPMock.get/2:
Mox.expect(PlugGateway.HTTPMock, :get, fn url, headers ->
try do
# Did PlugGateway.BackendClient compose the full URL?
assert url == "http://api.example.com/api/users"

# Did PlugGateway.BackendClient inject the authorization header?
assert {_, "Bearer BACKEND_AUTH_TOKEN"} = :lists.keyfind("authorization", 1, headers)

# Send an event matching the exception smuggling below:
send(test_pid, {:mock_result, :ok})

# Reply:
{:ok, 200, "yay"}
rescue
e ->
# Smuggle the exception back to the test so you get full colour diffs on assert failure:
send(test_pid, {:mock_result, :error, e, __STACKTRACE__})

# Reply as if the server didn't like what we sent:
{:ok, 400, "poot"}
end
end)

result = get("/users")

receive do
{:mock_result, :ok} -> :ok
{:mock_result, :error, e, stacktrace} -> reraise(e, stacktrace)
end

assert %{status: 200, resp_body: "yay"} = result
end

# Helpers

defp get(path) do
Expand Down
2 changes: 2 additions & 0 deletions plug_gateway/test/test_helper.exs
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
ExUnit.start()
Mox.defmock(PlugGateway.HTTPMock, for: PlugGateway.HTTP.API)
ExUnit.start(capture_log: true, timeout: 10_000, exclude: [:skip])

0 comments on commit b18a0ae

Please sign in to comment.