Created
September 2, 2019 02:48
-
-
Save ytnobody/fa8ffa7fc06f7f7e047f593e98dbd028 to your computer and use it in GitHub Desktop.
failed etude of elixir mock server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
iex(5)> sv = MyMock.run() | |
11:35:22.016 [info] Added handler for /user | |
11:35:22.016 [info] Added handler for /user/:id | |
%Mock.MockServer{ | |
name: :my_mock, | |
pid: #PID<0.765.0>, | |
port: 64400, | |
route: %{ | |
"/user" => %{"GET" => #Function<0.22167735/1 in MyMock.run/1>}, | |
"/user/:id" => %{ | |
"GET" => #Function<1.22167735/1 in MyMock.run/1>, | |
"POST" => #Function<2.22167735/1 in MyMock.run/1> | |
} | |
} | |
} | |
iex(6)> HTTPoison.get("http://127.0.0.1:64400/user/123") | |
{:ok, | |
%FakeServer.Response{ | |
body: "{\"id\": 123}", | |
headers: %{"Content-Type" => "application/json"}, | |
status: 200 | |
}} | |
{:ok, | |
%HTTPoison.Response{ | |
body: "", | |
headers: [{"content-length", "0"}], | |
request_url: "http://127.0.0.1:64400/user/123", | |
status_code: 500 | |
}} | |
iex(7)> | |
11:35:43.317 [error] Ranch protocol #PID<0.783.0> of listener :my_mock (connection #PID<0.782.0>, stream id 1) terminated | |
** (exit) :function_clause |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Mock.MockServer do | |
require Logger | |
alias FakeServer.Response | |
defstruct [ | |
:pid, | |
:name, | |
route: %{}, | |
port: 64400 | |
] | |
def build(name, port \\ nil), do: %__MODULE__{name: name, port: port || 64400} | |
def run(sv) do | |
case FakeServer.start(sv.name, sv.port) do | |
{:error, message} -> {:error, message} | |
{:ok, pid} -> | |
sv |> Map.put(:pid, pid) |> load_handlers() | |
end | |
end | |
def stop(sv) do | |
FakeServer.stop(sv.name) | |
end | |
def route_add(sv, method, path, code) do | |
entry = case sv.route |> Map.has_key?(path) do | |
false -> %{method => code} | |
true -> sv.route |> Map.fetch!(path) |> Map.put(method, code) | |
end | |
route = sv.route |> Map.put(path, entry) | |
sv |> Map.put(:route, route) | |
end | |
def get(sv, path, code), do: route_add(sv, "GET", path, code) | |
def post(sv, path, code), do: route_add(sv, "POST", path, code) | |
def put(sv, path, code), do: route_add(sv, "PUT", path, code) | |
def delete(sv, path, code), do: route_add(sv, "DELETE", path, code) | |
def patch(sv, path, code), do: route_add(sv, "PATCH", path, code) | |
defp build_handler(sv, path) do | |
case sv.route |> Map.fetch(path) do | |
:error -> {:error, "Bad entry #{path}"} | |
{:ok, entry} -> | |
fn (req) -> | |
res = case entry |> Map.fetch(req.method) do | |
:error -> Response.not_found!() | |
{:ok, code} -> code.(req) |> build_response() | |
end | |
res |> IO.inspect() | |
build_response(res) | |
end | |
end | |
end | |
defp build_handlers(sv) do | |
handler_map_list = sv.route |> Map.keys() |> Enum.map( | |
fn path -> | |
case build_handler(sv, path) do | |
{:error, message} -> {:error, message} | |
handler -> %{path: path, handler: handler} | |
end | |
end | |
) | |
bad_items = handler_map_list |> Enum.filter(fn handler -> is_tuple(handler) end) | |
case bad_items do | |
[] -> handler_map_list | |
_ -> | |
message = bad_items | |
|> Enum.map(fn item -> Tuple.to_list(item) |> Enum.at(1) end) | |
|> Enum.join("\n") | |
{:error, message} | |
end | |
end | |
defp build_response([status_code, headers, body]), do: Response.new(status_code, body, headers || %{"Content-type" => "text/html"}) | |
defp put_route(sv, handler_map) do | |
FakeServer.put_route(sv.name, handler_map.path, handler_map.handler) | |
end | |
defp load_handlers(sv) do | |
with pid when not is_nil(pid) <- sv.pid, | |
handler_map_list when not is_tuple(handler_map_list) <- build_handlers(sv) | |
do | |
handler_map_list |> Enum.each( | |
fn handler_map -> | |
case put_route(sv, handler_map) do | |
{:error, reason} -> Logger.error("Failed to add handler for #{handler_map.path}: #{reason}") | |
:ok -> Logger.info("Added handler for #{handler_map.path}") | |
end | |
end | |
) | |
sv | |
else | |
nil -> {:error, "Server not running"} | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule MyMock do | |
alias Mock.MockServer | |
def run(port \\ nil) do | |
MockServer.build(:my_mock, port) | |
|> MockServer.get( "/user", fn _req -> [200, %{"Content-Type" => "application/json"}, "{\"message\": \"Hello, world\"}"] end) | |
|> MockServer.get( "/user/:id", fn _req -> [200, %{"Content-Type" => "application/json"}, "{\"id\": 123}"] end) | |
|> MockServer.post("/user/:id", fn _req -> [200, %{"Content-Type" => "application/json"}, "{\"message\": \foo\"}"] end) | |
|> MockServer.run() | |
end | |
def stop(sv) do | |
MockServer.stop(sv) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment