File size: 5,513 Bytes
c9c49ab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
defmodule Plausible.HTTPClientTest do
  use ExUnit.Case, async: false

  alias Plausible.HTTPClient
  alias Plug.Conn

  setup do
    bypass = Bypass.open()
    {:ok, bypass: bypass}
  end

  test "get/2 works", %{bypass: bypass} do
    Bypass.expect_once(bypass, "GET", "/get", fn conn ->
      Conn.resp(conn, 200, "ok")
    end)

    assert {:ok, %Finch.Response{status: 200, body: "ok"}} =
             HTTPClient.get(bypass_url(bypass, path: "/get"))
  end

  test "post/3 works", %{bypass: bypass} do
    Bypass.expect_once(bypass, "POST", "/post", fn conn ->
      Conn.resp(conn, 200, "ok")
    end)

    assert {:ok, %Finch.Response{status: 200, body: "ok"}} =
             HTTPClient.post(bypass_url(bypass, path: "/post"))
  end

  test "post/3 doesn't alter params if binary passed",
       %{
         bypass: bypass
       } do
    body = "raw binary"
    headers = []

    Bypass.expect_once(bypass, "POST", "/post", fn conn ->
      opts = Plug.Parsers.init(parsers: [:urlencoded, {:json, json_decoder: Jason}])

      conn
      |> Plug.Parsers.call(opts)
      |> Conn.resp(200, body)
    end)

    assert {:ok, %Finch.Response{status: 200, body: ^body}} =
             HTTPClient.post(bypass_url(bypass, path: "/post"), headers, body)
  end

  test "post/3 URL-encodes params if request content-type is set to application/x-www-form-urlencoded and a map is supplied",
       %{
         bypass: bypass
       } do
    body = %{hello: :world, alice: :bob}
    headers = [{"Content-Type", "application/x-www-form-urlencoded"}]

    Bypass.expect_once(bypass, "POST", "/post", fn conn ->
      opts = Plug.Parsers.init(parsers: [:urlencoded])
      conn = Plug.Parsers.call(conn, opts)

      assert hd(Conn.get_req_header(conn, "content-type")) == "application/x-www-form-urlencoded"
      assert conn.body_params["hello"] == "world"
      assert conn.body_params["alice"] == "bob"
      Conn.resp(conn, 200, "ok")
    end)

    assert {:ok, %Finch.Response{status: 200, body: "ok"}} =
             HTTPClient.post(bypass_url(bypass, path: "/post"), headers, body)
  end

  test "post/3 JSON-encodes params if request content-type is other than application/x-www-form-urlencoded and a map is supplied",
       %{
         bypass: bypass
       } do
    params = %{hello: :world, alice: :bob}
    headers_no_content_type = [{"foo", "moo"}]
    headers_json = [{"Content-Type", "application/json"}]

    Bypass.expect_once(bypass, "POST", "/any", fn conn ->
      opts = Plug.Parsers.init(parsers: [:urlencoded, {:json, json_decoder: Jason}])
      conn = Plug.Parsers.call(conn, opts)

      assert Conn.get_req_header(conn, "content-type") == ["application/json"]
      assert conn.body_params["hello"] == "world"
      assert conn.body_params["alice"] == "bob"
      Conn.resp(conn, 200, "ok")
    end)

    Bypass.expect_once(bypass, "POST", "/json", fn conn ->
      opts = Plug.Parsers.init(parsers: [{:json, json_decoder: Jason}])
      conn = Plug.Parsers.call(conn, opts)

      assert Conn.get_req_header(conn, "content-type") == ["application/json"]
      assert conn.body_params["hello"] == "world"
      assert conn.body_params["alice"] == "bob"
      Conn.resp(conn, 200, "ok")
    end)

    assert {:ok, %Finch.Response{status: 200, body: "ok"}} =
             HTTPClient.post(bypass_url(bypass, path: "/json"), headers_json, params)

    assert {:ok, %Finch.Response{status: 200, body: "ok"}} =
             HTTPClient.post(bypass_url(bypass, path: "/any"), headers_no_content_type, params)
  end

  @tag :slow
  test "post/4 accepts finch request opts", %{bypass: bypass} do
    Bypass.expect_once(bypass, "POST", "/timeout", fn conn ->
      Process.sleep(500)
      Conn.resp(conn, 200, "ok")
    end)

    assert {:error, %Finch.TransportError{reason: :timeout}} =
             HTTPClient.post(bypass_url(bypass, path: "/timeout"), [], %{}, receive_timeout: 100)

    Bypass.pass(bypass)
  end

  test "non-200 responses are tagged as errors", %{bypass: bypass} do
    Bypass.expect_once(bypass, "GET", "/get", fn conn ->
      Conn.resp(conn, 300, "oops")
    end)

    assert {:error,
            %HTTPClient.Non200Error{
              reason: %Finch.Response{status: 300, body: "oops"}
            }} = HTTPClient.get(bypass_url(bypass, path: "/get"))

    Bypass.expect_once(bypass, "GET", "/get", fn conn ->
      Conn.resp(conn, 400, "oops")
    end)

    assert {:error,
            %HTTPClient.Non200Error{
              reason: %Finch.Response{status: 400, body: "oops"}
            }} = HTTPClient.get(bypass_url(bypass, path: "/get"))
  end

  test "header keys are downcased but values are not", %{bypass: bypass} do
    Bypass.expect_once(bypass, "GET", "/get", fn conn ->
      conn
      |> Conn.put_resp_header("Some-Header", "Header-Value")
      |> Conn.resp(200, "ok")
    end)

    assert {:ok, res} = HTTPClient.get(bypass_url(bypass, path: "/get"))
    assert {"some-header", "Header-Value"} in res.headers
  end

  defp bypass_url(bypass, opts) do
    port = bypass.port
    path = Keyword.get(opts, :path, "/")

    "http://localhost:#{port}#{path}"
  end

  test "decodes json in case content-type is found", %{bypass: bypass} do
    Bypass.expect_once(bypass, "GET", "/json", fn conn ->
      conn
      |> Conn.put_resp_header("content-type", "application/json; something")
      |> Conn.resp(200, """
      {"answer": 42}
      """)
    end)

    assert {:ok, %{body: %{"answer" => 42}}} = HTTPClient.get(bypass_url(bypass, path: "/json"))
  end
end