this repo has no description

Use Fresh to test WebSocket API

The only reasonable way to test the WS API I have found is to run real
client and do real connection. Unfortunately Phoenix do not provide any
solution to test custom transports for sockets.

hauleth.dev 8e739ab2 230fa8e1

verified
Changed files
+139 -5
config
lib
esl_hn
test
esl_hn
esl_hn_web
+2 -2
config/test.exs
···
# We don't run a server during test. If one is required,
# you can enable the server option below.
config :esl_hn, EslHnWeb.Endpoint,
-
http: [ip: {127, 0, 0, 1}, port: 4002],
+
http: [ip: {127, 0, 0, 1}, port: 0],
secret_key_base:
"6iR9shI35kN7Xr5bOLgBVMHXTZQS49Gwu82WW4rsr0uhaia7D+NjfNrhhvcOp4rr",
-
server: false
+
server: true
# Disable main refresher in tests
config :esl_hn, refresh: 0
+2 -1
lib/esl_hn/hn.ex
···
|> Task.async_stream(&story(&1, opts))
|> map_while(fn
{:ok, {:ok, data}} -> {:cont, data}
-
other -> {:halt, other}
+
{:ok, error} -> {:halt, error}
+
{:error, reason} -> {:halt, {:error, {:process_error, reason}}}
end)
|> case do
list when is_list(list) -> {:ok, list}
+4 -1
mix.exs
···
# Testing
{:test_server, "~> 0.1.21", only: [:test]},
-
{:stream_data, "~> 1.0", only: [:dev, :test]}
+
{:stream_data, "~> 1.0", only: [:dev, :test]},
+
# Websocket client for testing custom socket transport, I have no idea how
+
# ti can test it better
+
{:fresh, "~> 0.4.4", only: [:test]}
]
end
+2
mix.lock
···
"erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"},
"file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"},
"finch": {:hex, :finch, "0.20.0", "5330aefb6b010f424dcbbc4615d914e9e3deae40095e73ab0c1bb0968933cadf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2658131a74d051aabfcba936093c903b8e89da9a1b63e430bee62045fa9b2ee2"},
+
"fresh": {:hex, :fresh, "0.4.4", "9d67a1d97112e70f4dfabd63b40e4b182ef64dfa84a2d9ee175eb4e34591e9f7", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mint, "~> 1.5", [hex: :mint, repo: "hexpm", optional: false]}, {:mint_web_socket, "~> 1.0", [hex: :mint_web_socket, repo: "hexpm", optional: false]}], "hexpm", "ba21d3fa0aa77bf18ca397e4c851de7432bb3f9c170a1645a16e09e4bba54315"},
"hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"},
"mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"},
+
"mint_web_socket": {:hex, :mint_web_socket, "1.0.4", "0b539116dbb3d3f861cdf5e15e269a933cb501c113a14db7001a3157d96ffafd", [:mix], [{:mint, ">= 1.4.1 and < 2.0.0-0", [hex: :mint, repo: "hexpm", optional: false]}], "hexpm", "027d4c5529c45a4ba0ce27a01c0f35f284a5468519c045ca15f43decb360a991"},
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
"phoenix": {:hex, :phoenix, "1.7.21", "14ca4f1071a5f65121217d6b57ac5712d1857e40a0833aff7a691b7870fc9a3b", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "336dce4f86cba56fed312a7d280bf2282c720abb6074bdb1b61ec8095bdd0bc9"},
+64 -1
test/esl_hn/hn_test.exs
···
assert {:error, {:http_response, 418, _}} =
@subject.top_stories_ids(base_url: TestServer.url())
end
+
+
test "returns error in case of network error" do
+
assert {:error, _} =
+
@subject.top_stories_ids(
+
adapter: fn request ->
+
{request, %RuntimeError{message: "oops"}}
+
end
+
)
+
end
end
describe "story/2" do
-
test "returns error on non 200 response" do
+
test "returns story on 200 response" do
data = %{
id: 2137,
title: "Foo"
···
assert {:ok, %{title: "Foo"}} =
@subject.story(2137, base_url: TestServer.url())
+
end
+
+
test "returns error in case of network error" do
+
assert {:error, _} =
+
@subject.story(2137,
+
adapter: fn request ->
+
{request, %RuntimeError{message: "oops"}}
+
end
+
)
+
end
+
end
+
+
describe "stories/2" do
+
test "returns stories on 200 response" do
+
TestServer.add("/item/2137.json",
+
to: fn conn ->
+
Phoenix.Controller.json(conn, %{
+
id: 2137,
+
title: "Foo"
+
})
+
end
+
)
+
+
TestServer.add("/item/666.json",
+
to: fn conn ->
+
Phoenix.Controller.json(conn, %{
+
id: 666,
+
title: "Bar"
+
})
+
end
+
)
+
+
assert {:ok, [_, _]} =
+
@subject.stories([2137, 666], base_url: TestServer.url())
+
end
+
+
test "returns error if at least one story returns error" do
+
TestServer.add("/item/2137.json",
+
to: fn conn ->
+
Phoenix.Controller.json(conn, %{
+
id: 2137,
+
title: "Foo"
+
})
+
end
+
)
+
+
TestServer.add("/item/666.json",
+
to: fn conn ->
+
Conn.send_resp(conn, 404, "")
+
end
+
)
+
+
assert {:error, _} =
+
@subject.stories([2137, 666], base_url: TestServer.url())
end
end
end
+65
test/esl_hn_web/api/socket_test.exs
···
+
defmodule EslHnWeb.Api.SocketTest do
+
use ExUnit.Case, async: false
+
+
import EslHn.Test.Data
+
+
alias EslHn.Cache
+
+
setup do
+
assert {:ok, {host, port}} = EslHnWeb.Endpoint.server_info(:http)
+
+
on_exit(fn -> Cache.flush(EslHn) end)
+
+
{:ok, host: host, port: port}
+
end
+
+
defmodule Client do
+
use Fresh
+
+
def handle_in({:text, data}, {parent, counter}) do
+
send(parent, {:ws, counter, JSON.decode!(data)})
+
{:ok, {parent, counter + 1}}
+
end
+
end
+
+
test "receive initial list of stories", ctx do
+
stories = Enum.take(story(), 50)
+
+
ids = Enum.map(stories, & &1.id)
+
Cache.write(EslHn, :index, ids)
+
+
Cache.write_all(EslHn, Enum.map(stories, &{&1.id, &1}))
+
+
start_supervised!(
+
{Client, uri: "ws://localhost:#{ctx.port}/websocket", state: {self(), 0}}
+
)
+
+
assert_receive {:ws, 0, init}
+
+
recv_ids = Enum.map(init, & &1["id"])
+
+
assert Enum.all?(ids, &(&1 in recv_ids))
+
assert length(recv_ids) == length(ids)
+
end
+
+
test "broadcasted events are sent over socket", ctx do
+
start_supervised!(
+
{Client, uri: "ws://localhost:#{ctx.port}/websocket", state: {self(), 0}}
+
)
+
+
assert_receive {:ws, 0, []}
+
+
for stories <- Enum.take(StreamData.list_of(story(), min_length: 1), 10) do
+
ids = Enum.map(stories, & &1.id)
+
+
EslHn.broadcast_new(stories)
+
+
assert_receive {:ws, _, msg}
+
+
recv_ids = Enum.map(msg, & &1["id"])
+
+
assert Enum.all?(ids, &(&1 in recv_ids))
+
assert length(recv_ids) == length(ids)
+
end
+
end
+
end