/
webhook_plug_test.exs
123 lines (98 loc) · 3.01 KB
/
webhook_plug_test.exs
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
122
123
defmodule Stripe.WebhookPlugTest do
use ExUnit.Case
use Plug.Test
alias Stripe.WebhookPlug
@valid_payload ~S({"object": "event"})
@secret "secret"
@opts WebhookPlug.init(
at: "/webhook/stripe",
handler: __MODULE__.Handler,
secret: @secret
)
defmodule Handler do
@behaviour Stripe.WebhookHandler
@impl true
def handle_event(%Stripe.Event{object: "event"}), do: :ok
end
defmodule BadHandler do
def handle_event(_), do: nil
end
def get_value(:secret), do: @secret
defp generate_signature_header(payload) do
timestamp = System.system_time(:second)
# TODO: remove when we require OTP 22
code = case System.otp_release() >= "22" do
true -> :crypto.mac(:hmac, :sha256, @secret, "#{timestamp}.#{payload}")
false -> :crypto.mac(:sha256, @secret, "#{timestamp}.#{payload}")
end
signature =
code
|> Base.encode16(case: :lower)
"t=#{timestamp},v1=#{signature}"
end
describe "WebhookPlug" do
setup do
signature_header = generate_signature_header(@valid_payload)
conn =
conn(:post, "/webhook/stripe", @valid_payload)
|> put_req_header("stripe-signature", signature_header)
{:ok, %{conn: conn}}
end
test "accepts valid signature", %{conn: conn} do
result = WebhookPlug.call(conn, @opts)
assert result.state == :sent
assert result.status == 200
end
test "rejects invalid signature", %{conn: conn} do
signature_header = generate_signature_header("random")
conn = put_req_header(conn, "stripe-signature", signature_header)
result = WebhookPlug.call(conn, @opts)
assert result.state == :sent
assert result.status == 400
end
test "nil secret should raise an error", %{conn: conn} do
opts =
WebhookPlug.init(
at: "/webhook/stripe",
handler: __MODULE__.Handler,
secret: nil
)
assert_raise RuntimeError, fn ->
WebhookPlug.call(conn, opts)
end
end
test "function secret should be evaluated", %{conn: conn} do
opts =
WebhookPlug.init(
at: "/webhook/stripe",
handler: __MODULE__.Handler,
secret: fn -> @secret end
)
result = WebhookPlug.call(conn, opts)
assert result.state == :sent
assert result.status == 200
end
test "{m, f, a} secret should be evaluated", %{conn: conn} do
opts =
WebhookPlug.init(
at: "/webhook/stripe",
handler: __MODULE__.Handler,
secret: {__MODULE__, :get_value, [:secret]}
)
result = WebhookPlug.call(conn, opts)
assert result.state == :sent
assert result.status == 200
end
test "crash hard if handler fails", %{conn: conn} do
opts =
WebhookPlug.init(
at: "/webhook/stripe",
handler: __MODULE__.BadHandler,
secret: @secret
)
assert_raise RuntimeError, fn ->
WebhookPlug.call(conn, opts)
end
end
end
end