shahryarjb
Req application/octet-stream tar file mock test
Hello friends, Imagine you want to download a tar file from hex.pm website.
I have this function and do not want to install req hex plugin. as you see I find the "contents.tar.gz" and it is the binary of file.
@spec download(download_type, pkg) :: okey_return | error_return
def download(:hex, %{app: app, tag: tag_name}) when not is_nil(tag_name) do
case build_url("https://repo.hex.pm/tarballs/#{app}-#{tag_name}.tar") do
%Req.Response{status: 200, body: body} ->
converted = Map.new(body, fn {key, value} -> {to_string(key), value} end)
{:ok, converted["contents.tar.gz"]}
_ ->
mix_global_err()
end
end
And downloader function:
defp build_url(location) do
[base_url: location]
|> Keyword.merge(Application.get_env(:mishka_installer, :downloader_req_options, []))
|> Keyword.merge(Application.get_env(:mishka_installer, :proxy, []))
|> Req.request!()
end
Now I want to mock it, so this is the place I can not be able to fix.
One of my function test is what does not work:
body = [
{~c"VERSION", "3"},
{~c"CHECKSUM", "1D5EC32825E5AF1B5CF58933F4C918909BC79E4391EBE2B9315B02705145B18F"},
{~c"metadata.config", "{<<\"app\">>,<<\"mishka_installer\">>}.\n{<<\"build_tools\">>"},
{~c"contents.tar.gz", <<31, 139, 8, 0, 0, 0>>}
]
body_iolist_to_binary =
Enum.reduce(body, "", fn {key, value}, acc -> acc <> "#{key}:#{value}\n" end)
|> :erlang.iolist_to_binary()
Req.Test.expect(Downloader, fn conn ->
conn
|> Plug.Conn.put_resp_content_type("application/octet-stream")
|> Plug.Conn.send_resp(200, body_iolist_to_binary)
end)
If I put body directly I have error!! I have no idea how can simulate the hex output.
It should be noted my function works as well, my problem is located in testing this
Thank you in advance
Most Liked
wojtekmach
Even if you end up not using req_hex, I’d still consider using hex_core to get more realistic tests. Here is an example:
Mix.install([
:req,
:plug,
:hex_core
])
Req.Test.expect(Downloader, fn conn ->
{:ok, %{tarball: tarball}} =
:hex_tarball.create(
%{
"name" => "foo",
"version" => "1.0.0"
},
[
{~c"mix.exs",
"""
defmodule Foo.MixProject do
use Mix.Project
def project do
[app: :foo, version: "1.0.0"]
end
end
"""}
]
)
conn
|> Plug.Conn.put_resp_content_type("application/octet-stream", nil)
|> Plug.Conn.send_resp(200, tarball)
end)
Req.get!(url: "/foo.tar", plug: {Req.Test, Downloader})
|> IO.inspect()
Thanks @LostKobrakai for reporting the format detection bug btw. application/octet-stream; charset=utf-8 is not a proper content-type for tar, these bytes are not utf8. I’ll see if I can make any improvements anyway though.
LostKobrakai
Good point. I didn’t really think about that tbh. So Plug.Conn.put_resp_content_type(conn, "application/octet-stream", nil) is actually the way to go then, not just a workaround.
LostKobrakai
A tar file is not some kind of map, like you’re building here in your test. The body you get from Req has the tar already extracted, which gives you a map (see Req.Steps — req v0.5.0). In your mock you need to build a proper .tar file, e.g. by using :erl_tar.
LostKobrakai
This looks to be a bug. Tracing in Req it determines tar as format on the real request, but bin for the mocked request – hence no automatic decoding happening. You could work around this by disabling the built in decoding and doing it manually.







