| 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 |
|
|