-
Notifications
You must be signed in to change notification settings - Fork 108
/
bypass.ex
121 lines (96 loc) · 3.72 KB
/
bypass.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
defmodule Bypass do
@moduledoc """
Bypass provides a quick way to create a custom Plug that can be put
in place instead of an actual HTTP server to return prebaked responses
to client requests.
This module is the main interface to the library.
"""
defstruct pid: nil, port: nil
import Bypass.Utils
require Logger
@doc """
Starts an Elixir process running a minimal Plug app. The process
is a HTTP handler and listens to requests on a TPC port on localhost.
Use the other functions in this module to declare which requests are
handled and set expectations on the calls.
"""
def open(opts \\ []) do
case Supervisor.start_child(Bypass.Supervisor, [opts]) do
{:ok, pid} ->
port = Bypass.Instance.call(pid, :port)
debug_log "Did open connection #{inspect pid} on port #{inspect port}"
bypass = %Bypass{pid: pid, port: port}
setup_framework_integration(test_framework(), bypass)
bypass
other ->
other
end
end
# Raise an error if called with an unknown framework
#
defp setup_framework_integration(:ex_unit, bypass = %{pid: pid}) do
ExUnit.Callbacks.on_exit({Bypass, pid}, fn ->
do_verify_expectations(bypass.pid, ExUnit.AssertionError)
end)
end
defp setup_framework_integration(:espec, _bypass) do
# Entry point for more advanced ESpec configurations
end
@doc """
Can be called to immediately verify if the declared request
expectations have been met.
Returns `:ok` on success and raises an error on failure.
"""
def verify_expectations!(bypass) do
verify_expectations!(test_framework(), bypass)
end
defp verify_expectations!(:ex_unit, _bypass) do
raise "Not available in ExUnit, as it's configured automatically."
end
if Code.ensure_loaded?(ESpec) do
defp verify_expectations!(:espec, bypass) do
do_verify_expectations(bypass.pid, ESpec.AssertionError)
end
end
defp do_verify_expectations(bypass_pid, error_module) do
case Bypass.Instance.call(bypass_pid, :on_exit) do
:ok ->
:ok
:ok_call ->
:ok
{:error, :too_many_requests, {:any, :any}} ->
raise error_module, "Expected only one HTTP request for Bypass"
{:error, :too_many_requests, {method, path}} ->
raise error_module, "Expected only one HTTP request for Bypass at #{method} #{path}"
{:error, :unexpected_request, {:any, :any}} ->
raise error_module, "Bypass got an HTTP request but wasn't expecting one"
{:error, :unexpected_request, {method, path}} ->
raise error_module,
"Bypass got an HTTP request but wasn't expecting one at #{method} #{path}"
{:error, :not_called, {:any, :any}} ->
raise error_module, "No HTTP request arrived at Bypass"
{:error, :not_called, {method, path}} ->
raise error_module,
"No HTTP request arrived at Bypass at #{method} #{path}"
{:exit, {class, reason, stacktrace}} ->
:erlang.raise(class, reason, stacktrace)
end
end
def up(%Bypass{pid: pid}),
do: Bypass.Instance.call(pid, :up)
def down(%Bypass{pid: pid}),
do: Bypass.Instance.call(pid, :down)
def expect(%Bypass{pid: pid}, fun),
do: Bypass.Instance.call(pid, {:expect, fun})
def expect(%Bypass{pid: pid}, methods, paths, fun),
do: Bypass.Instance.call(pid, {:expect, methods, paths, fun})
def expect_once(%Bypass{pid: pid}, fun),
do: Bypass.Instance.call(pid, {:expect_once, fun})
def expect_once(%Bypass{pid: pid}, methods, paths, fun),
do: Bypass.Instance.call(pid, {:expect_once, methods, paths, fun})
def pass(%Bypass{pid: pid}),
do: Bypass.Instance.call(pid, :pass)
defp test_framework do
Application.get_env(:bypass, :test_framework, :ex_unit)
end
end