Skip to content

Commit e41649c

Browse files
authored
Replace hackney with httpc (#311)
* Replace hackney with httpc * SSL options * FIXUP * Cache fixed * Aaaah, caching again * FIXUP * Add missing apps to :extra_applications * Add better check for :public_key
1 parent 509c526 commit e41649c

File tree

5 files changed

+164
-66
lines changed

5 files changed

+164
-66
lines changed

.github/workflows/tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ jobs:
2525
- uses: actions/cache@v3
2626
with:
2727
path: deps
28-
key: ${{ runner.os }}-mix-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
28+
key: ${{ runner.os }}-${{ matrix.elixir }}-otp${{ matrix.otp }}-mix-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
2929
restore-keys: |
30-
${{ runner.os }}-mix-
30+
${{ runner.os }}-${{ matrix.elixir }}-otp${{ matrix.otp }}-mix-
3131
- run: mix deps.get
3232
- run: mix coveralls.github

lib/excoveralls/poster.ex

Lines changed: 114 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,10 @@ defmodule ExCoveralls.Poster do
55
@file_name "excoveralls.post.json.gz"
66

77
@doc """
8-
Create a temporarily json file and post it to server using hackney library.
9-
Then, remove the file after it's completed.
8+
Compresses the given `json` and posts it to the coveralls server.
109
"""
1110
def execute(json, options \\ []) do
12-
File.write!(@file_name, json |> :zlib.gzip())
13-
response = send_file(@file_name, options)
14-
File.rm!(@file_name)
15-
16-
case response do
11+
case json |> :zlib.gzip() |> upload_zipped_json(options) do
1712
{:ok, message} ->
1813
IO.puts(message)
1914

@@ -22,44 +17,129 @@ defmodule ExCoveralls.Poster do
2217
end
2318
end
2419

25-
defp send_file(file_name, options) do
26-
Application.ensure_all_started(:hackney)
20+
defp upload_zipped_json(content, options) do
21+
Application.ensure_all_started(:ssl)
22+
Application.ensure_all_started(:httpc)
23+
Application.ensure_all_started(:inets)
24+
2725
endpoint = options[:endpoint] || "https://coveralls.io"
2826

29-
response =
30-
:hackney.request(
31-
:post,
32-
"#{endpoint}/api/v1/jobs",
33-
[],
34-
{:multipart,
35-
[
36-
{:file, file_name, {"form-data", [{"name", "json_file"}, {"filename", file_name}]},
37-
[{"Content-Type", "gzip/json"}]}
38-
]},
39-
[{:recv_timeout, 10_000}]
40-
)
41-
42-
case response do
43-
{:ok, status_code, _, _} when status_code in 200..299 ->
27+
multipart_boundary =
28+
"---------------------------" <> Base.encode16(:crypto.strong_rand_bytes(8), case: :lower)
29+
30+
body =
31+
[
32+
"--#{multipart_boundary}",
33+
"content-length: #{byte_size(content)}",
34+
"content-disposition: form-data; name=json_file; filename=#{@file_name}",
35+
"content-type: gzip/json",
36+
"",
37+
content,
38+
"--#{multipart_boundary}--"
39+
]
40+
|> Enum.join("\r\n")
41+
42+
headers = [
43+
{~c"Host", String.to_charlist(URI.parse(endpoint).host)},
44+
{~c"User-Agent", ~c"excoveralls"},
45+
{~c"Content-Length", String.to_charlist(Integer.to_string(byte_size(body)))},
46+
{~c"Accept", ~c"*/*"}
47+
]
48+
49+
# All header names and values MUST be charlists in older OTP versions. In newer versions,
50+
# binaries are fine. This is hard to debug because httpc simply *hangs* on older OTP
51+
# versions if you use a binary value.
52+
if Enum.any?(headers, fn {_, val} -> not is_list(val) end) do
53+
raise "all header names and values must be charlists"
54+
end
55+
56+
request = {
57+
String.to_charlist(endpoint) ++ ~c"/api/v1/jobs",
58+
headers,
59+
_content_type = ~c"multipart/form-data; boundary=#{multipart_boundary}",
60+
body
61+
}
62+
63+
http_options = [
64+
timeout: 10_000,
65+
ssl:
66+
[
67+
verify: :verify_peer,
68+
depth: 2,
69+
customize_hostname_check: [
70+
match_fun: :public_key.pkix_verify_hostname_match_fun(:https)
71+
]
72+
# https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/inets
73+
] ++ cacert_option()
74+
]
75+
76+
case :httpc.request(:post, request, http_options, sync: true, body_format: :binary) do
77+
{:ok, {{_protocol, status_code, _status_message}, _headers, _body}}
78+
when status_code in 200..299 ->
4479
{:ok, "Successfully uploaded the report to '#{endpoint}'."}
4580

46-
{:ok, 500 = _status_code, _, _client} ->
47-
{:ok, "API endpoint `#{endpoint}` is not available and return internal server error! Ignoring upload"}
48-
{:ok, 405 = _status_code, _, _client} ->
81+
{:ok, {{_protocol, 500, _status_message}, _headers, _body}} ->
82+
{:ok,
83+
"API endpoint `#{endpoint}` is not available and return internal server error! Ignoring upload"}
84+
85+
{:ok, {{_protocol, 405, _status_message}, _headers, _body}} ->
4986
{:ok, "API endpoint `#{endpoint}` is not available due to maintenance! Ignoring upload"}
50-
{:ok, status_code, _, client} ->
51-
{:ok, body} = :hackney.body(client)
5287

88+
{:ok, {{_protocol, status_code, _status_message}, _headers, body}} ->
5389
{:error,
54-
"Failed to upload the report to '#{endpoint}' (reason: status_code = #{status_code}, body = #{
55-
body
56-
})."}
90+
"Failed to upload the report to '#{endpoint}' (reason: status_code = #{status_code}, body = #{body})."}
5791

58-
{:error, reason} when reason in [:timeout, :connect_timeout] ->
59-
{:ok, "Unable to upload the report to '#{endpoint}' due to a timeout. Not failing the build."}
92+
{:error, reason} when reason in [:timeout, :connect_timeout] ->
93+
{:ok,
94+
"Unable to upload the report to '#{endpoint}' due to a timeout. Not failing the build."}
6095

6196
{:error, reason} ->
6297
{:error, "Failed to upload the report to '#{endpoint}' (reason: #{inspect(reason)})."}
6398
end
6499
end
100+
101+
# TODO: remove this once we depend on an Elixir version that requires OTP 25+.
102+
if System.otp_release() >= "25" do
103+
defp cacert_option do
104+
if Code.ensure_loaded?(CAStore) do
105+
[cacertfile: String.to_charlist(CAStore.file_path())]
106+
else
107+
case :public_key.cacerts_load() do
108+
:ok ->
109+
[cacerts: :public_key.cacerts_get()]
110+
111+
{:error, reason} ->
112+
raise ExCoveralls.ReportUploadError,
113+
message: """
114+
Failed to load OS certificates. We tried to use OS certificates because we
115+
couldn't find the :castore library. If you want to use :castore, please add
116+
117+
{:castore, "~> 1.0"}
118+
119+
to your dependencies. Otherwise, make sure you can load OS certificates by
120+
running :public_key.cacerts_load() and checking the result. The error we
121+
got was:
122+
123+
#{inspect(reason)}
124+
"""
125+
end
126+
end
127+
end
128+
else
129+
defp cacert_option do
130+
if Code.ensure_loaded?(CAStore) do
131+
[cacertfile: String.to_charlist(CAStore.file_path())]
132+
else
133+
raise ExCoveralls.ReportUploadError,
134+
message: """
135+
Failed to use any SSL certificates. We didn't find the :castore library,
136+
and we couldn't use OS certificates because that requires OTP 25 or later.
137+
If you want to use :castore, please add
138+
139+
{:castore, "~> 1.0"}
140+
141+
"""
142+
end
143+
end
144+
end
65145
end

mix.exs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,17 @@ defmodule ExCoveralls.Mixfile do
3030
end
3131

3232
def application do
33-
[extra_applications: [:eex, :tools, :xmerl]]
33+
[extra_applications: [:eex, :tools, :xmerl, :inets, :ssl, :public_key]]
3434
end
3535

3636
defp elixirc_paths(:test), do: ["lib", "test/fixtures/test_missing.ex"]
3737
defp elixirc_paths(_), do: ["lib"]
3838

39-
def deps do
39+
defp deps do
4040
[
41+
{:castore, "~> 1.0", optional: true},
4142
{:jason, "~> 1.0"},
42-
{:hackney, "~> 1.16"},
43+
{:bypass, "~> 2.1.0", only: :test},
4344
{:ex_doc, ">= 0.0.0", only: :dev, runtime: false},
4445
{:meck, "~> 0.8", only: :test},
4546
{:mock, "~> 0.3.6", only: :test},

mix.lock

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
%{
2-
"certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
3-
"earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"},
2+
"bypass": {:hex, :bypass, "2.1.0", "909782781bf8e20ee86a9cabde36b259d44af8b9f38756173e8f5e2e1fabb9b1", [:mix], [{:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "d9b5df8fa5b7a6efa08384e9bbecfe4ce61c77d28a4282f79e02f1ef78d96b80"},
3+
"castore": {:hex, :castore, "1.0.3", "7130ba6d24c8424014194676d608cb989f62ef8039efd50ff4b3f33286d06db8", [:mix], [], "hexpm", "680ab01ef5d15b161ed6a95449fac5c6b8f60055677a8e79acf01b27baa4390b"},
4+
"cowboy": {:hex, :cowboy, "2.10.0", "ff9ffeff91dae4ae270dd975642997afe2a1179d94b1887863e43f681a203e26", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"},
5+
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
6+
"cowlib": {:hex, :cowlib, "2.12.1", "a9fa9a625f1d2025fe6b462cb865881329b5caff8f1854d1cbc9f9533f00e1e1", [:make, :rebar3], [], "hexpm", "163b73f6367a7341b33c794c4e88e7dbfe6498ac42dcd69ef44c5bc5507c8db0"},
47
"earmark_parser": {:hex, :earmark_parser, "1.4.12", "b245e875ec0a311a342320da0551da407d9d2b65d98f7a9597ae078615af3449", [:mix], [], "hexpm", "711e2cc4d64abb7d566d43f54b78f7dc129308a63bc103fbd88550d2174b3160"},
58
"ex_doc": {:hex, :ex_doc, "0.23.0", "a069bc9b0bf8efe323ecde8c0d62afc13d308b1fa3d228b65bca5cf8703a529d", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "f5e2c4702468b2fd11b10d39416ddadd2fcdd173ba2a0285ebd92c39827a5a16"},
6-
"hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~> 2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"},
7-
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
89
"jason": {:hex, :jason, "1.1.1", "d3ccb840dfb06f2f90a6d335b536dd074db748b3e7f5b11ab61d239506585eb2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "639645cfac325e34938167b272bae0791fea3a34cf32c29525abf1d323ed4c18"},
910
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
1011
"makeup_elixir": {:hex, :makeup_elixir, "0.15.0", "98312c9f0d3730fde4049985a1105da5155bfe5c11e47bdc7406d88e01e4219b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "75ffa34ab1056b7e24844c90bfc62aaf6f3a37a15faa76b07bc5eba27e4a8b4a"},
1112
"meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm", "d34f013c156db51ad57cc556891b9720e6a1c1df5fe2e15af999c84d6cebeb1a"},
12-
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
13-
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
13+
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
1414
"mock": {:hex, :mock, "0.3.6", "e810a91fabc7adf63ab5fdbec5d9d3b492413b8cda5131a2a8aa34b4185eb9b4", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "bcf1d0a6826fb5aee01bae3d74474669a3fa8b2df274d094af54a25266a1ebd2"},
1515
"nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"},
16-
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
16+
"plug": {:hex, :plug, "1.14.2", "cff7d4ec45b4ae176a227acd94a7ab536d9b37b942c8e8fa6dfc0fff98ff4d80", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "842fc50187e13cf4ac3b253d47d9474ed6c296a8732752835ce4a86acdf68d13"},
17+
"plug_cowboy": {:hex, :plug_cowboy, "2.6.1", "9a3bbfceeb65eff5f39dab529e5cd79137ac36e913c02067dba3963a26efe9b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"},
18+
"plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"},
19+
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
1720
"sax_map": {:hex, :sax_map, "1.0.1", "51a9382d741504c34d49118fb36d691c303d042e1da88f8edae8ebe75fe74435", [:mix], [{:saxy, "~> 1.0", [hex: :saxy, repo: "hexpm", optional: false]}], "hexpm", "a7c57c25d23bfc3ce93cf93400dcfb447fe463d27ee8c6913545161e78dc487a"},
1821
"saxy": {:hex, :saxy, "0.10.0", "38879f46a595862c22114792c71379355ecfcfa0f713b1cfcc59e1d4127f1f55", [:mix], [], "hexpm", "da130ed576e9f53d1a986ec5bd2fa72c1599501ede7d7a2dceb81acf53bf9790"},
19-
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
20-
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
22+
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
2123
}

test/poster_test.exs

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,51 @@
11
defmodule PosterTest do
22
use ExUnit.Case
3-
import Mock
43
import ExUnit.CaptureIO
5-
6-
test_with_mock "post json", :hackney, [request: fn(_, _, _, _, _) -> {:ok, 200, "", ""} end] do
4+
5+
setup do
6+
bypass = Bypass.open()
7+
%{bypass: bypass, endpoint: "http://localhost:#{bypass.port}"}
8+
end
9+
10+
test "successfully posting JSON", %{bypass: bypass, endpoint: endpoint} do
11+
Bypass.expect(bypass, fn conn ->
12+
assert conn.method == "POST"
13+
assert {"host", "localhost"} in conn.req_headers
14+
Plug.Conn.resp(conn, 200, "")
15+
end)
16+
717
assert capture_io(fn ->
8-
ExCoveralls.Poster.execute("json")
9-
end) =~ ~r/Successfully uploaded/
18+
ExCoveralls.Poster.execute("{}", endpoint: endpoint)
19+
end) =~ "Successfully uploaded"
1020
end
1121

12-
test_with_mock "post json fails", :hackney, [request: fn(_, _, _, _, _) -> {:error, "failed"} end] do
22+
test "post JSON fails", %{bypass: bypass, endpoint: endpoint} do
23+
Bypass.down(bypass)
24+
1325
assert_raise ExCoveralls.ReportUploadError, fn ->
14-
ExCoveralls.Poster.execute("json")
26+
ExCoveralls.Poster.execute("{}", endpoint: endpoint)
1527
end
1628
end
1729

18-
test_with_mock "post json timeout", :hackney, [request: fn(_, _, _, _, _) -> {:error, :timeout} end,
19-
request: fn(_, _, _, _, _) -> {:error, :connect_timeout} end] do
20-
assert capture_io(fn ->
21-
assert ExCoveralls.Poster.execute("json") == :ok
22-
end) =~ ~r/timeout/
23-
end
24-
25-
test_with_mock "post json fails due internal server error", :hackney, [request: fn(_, _, _, _, _) -> {:ok, 500, "", ""} end] do
30+
test "post JSON fails due internal server error", %{bypass: bypass, endpoint: endpoint} do
31+
Bypass.expect(bypass, fn conn ->
32+
assert conn.method == "POST"
33+
Plug.Conn.resp(conn, 500, "")
34+
end)
35+
2636
assert capture_io(fn ->
27-
assert ExCoveralls.Poster.execute("json") == :ok
37+
assert ExCoveralls.Poster.execute("{}", endpoint: endpoint) == :ok
2838
end) =~ ~r/internal server error/
2939
end
3040

31-
test_with_mock "post json fails due to maintenance", :hackney, [request: fn(_, _, _, _, _) -> {:ok, 405, "", ""} end] do
41+
test "post JSON fails due to maintenance", %{bypass: bypass, endpoint: endpoint} do
42+
Bypass.expect(bypass, fn conn ->
43+
assert conn.method == "POST"
44+
Plug.Conn.resp(conn, 405, "")
45+
end)
46+
3247
assert capture_io(fn ->
33-
assert ExCoveralls.Poster.execute("json") == :ok
48+
assert ExCoveralls.Poster.execute("{}", endpoint: endpoint) == :ok
3449
end) =~ ~r/maintenance/
3550
end
3651
end

0 commit comments

Comments
 (0)