0

I would like to create a mocked HTTP request for mix test of App.Controller.sign_up/2. How to match %Plug.Conn{adapter: {Plug.Adapters.Test.Conn ... in protocol's for:?

Caller

defmodule App.Controller do
  use App.Web, :controller
  def sign_up(conn, params) do
    case App.AppHelper.verify(conn, params) do
      {:ok, _} ->
    ...
    end
...

Helper

defmodule App.AppHelper do
  def verify(%Plug.Conn{adapter: {adapter, _}} = conn, params) do
    App.AppRequest.request adapter, params
  end
end

Protocol and Implementations

defprotocol App.AppRequest do
  @fallback_to_any true
  def request(adapter, params)
end

defimpl App.AppRequest, for: Plug.Adapters.Cowboy.Conn do
  def request(_, params) do
    #returns http request result
  end
end

defimpl App.AppRequest, for: Plug.Adapters.Test.Conn do
  def request(_, params) do
    #returns mocked result
  end
end

Ps. edited to my current state with help of @Dogbert

Resulted with

** (Protocol.UndefinedError) protocol App.AppRequest not implemented for Plug.Adapters.Test.Conn

ardhitama
  • 1,949
  • 2
  • 18
  • 29
  • (My answer was completely wrong so I've deleted it). Is there any reason you can't just add the 2 cases to the same function directly? Do you want this behavior to be extensible for other adapters? – Dogbert Oct 29 '16 at 06:54
  • @Dogbert I want to create a http request mock for testing a functionality involving out of band auth, basically I want to separate the test domain from app's – ardhitama Oct 29 '16 at 06:57
  • @Dogbert your answer was 99% correct, I have no clue why did you decide it’s wrong. 1. There is no need to include `Any` to the implementation, it will fall back to it. 2. Matching should be done by adapter type as Dogbert suggested. 3. The problem I guess is that you have `Test` defined somewhere in not visible to this code place. – Aleksei Matiushkin Oct 29 '16 at 07:02
  • Try `rm -rf _build` and compile it from the scratch. **Protocol consolidation** is tricky in Elixir. Make sure you’ve seen the “protocols consolidated” message with `Test` listed. – Aleksei Matiushkin Oct 29 '16 at 07:06
  • @mudasobwa didn't help, and not found `protocols consolidated` – ardhitama Oct 29 '16 at 07:08
  • It should. Have you seen “protocols consolidated” in compilation stage? UPD: not found “protocols consolidated” ⇐ that’s the problem. Unless you have it seen, nothing will work. Try `mix clear`. I don’t remember the exact set of commands to force it, but that’s definitely an issue. – Aleksei Matiushkin Oct 29 '16 at 07:10
  • Also, you might try explicit [`Protocol#consolidate/2`](http://elixir-lang.org/docs/stable/elixir/Protocol.html#consolidate/2) to make sure it works with it. – Aleksei Matiushkin Oct 29 '16 at 07:13
  • @mudasobwa have tried delete `_build` and `mix clean`, same problem. Not sure how to invoke `Protocol.consolidate/2` – ardhitama Oct 29 '16 at 07:19
  • Should I add this as elixir issue? – ardhitama Oct 29 '16 at 07:20
  • Not yet. Where `Plug.Adapters.Test.Conn` itself is defined? Be aware of that `spec` folder is not loaded by default. – Aleksei Matiushkin Oct 29 '16 at 07:23
  • @mudasobwa it's in `/deps/plug/lib/plug/adapters/test/conn.ex` – ardhitama Oct 29 '16 at 07:25
  • Would you mind to move it into `/lib/plug/adapters/test/conn.ex`, make sure everything is working properly, kindly ask Dogbert to reopen his answer since it was absolutely correct and post another question about “protocol defined in non-standard place is not consolidated”? I think the problem stated in this particular question is sorta solved (if it works being defined in root `/lib/` folder.) – Aleksei Matiushkin Oct 29 '16 at 07:31
  • @mudasobwa I thought it was correct too, but `defimpl` works on struct modules and instances of that, not plain different atoms. `conn.adapter` here is just a module, not a struct. – Dogbert Oct 29 '16 at 07:36
  • @mudasobwa bring it up to root doesn't seems working either. Though need someone else to validate, I'm using `umbrella` if that even matter. – ardhitama Oct 29 '16 at 07:42
  • @Dogbert huh? https://github.com/elixir-lang/elixir/search?utf8=%E2%9C%93&q=%22defimpl+List.Chars%22 – Aleksei Matiushkin Oct 29 '16 at 07:45
  • @mudasobwa https://gist.github.com/anonymous/d859a45caa45e8566bf6e5b1256fa7fd – Dogbert Oct 29 '16 at 07:48
  • @Dogbert ah, yes, indeed, I got it (“structs” term in your last comment was misleading, sorry.) So what we need is to use the _instance_ of something instead of plain atom, denoting the structure. As an dirty hack, `App.AppRequest.request Code.eval_string("%#{adapter}{}"), params` would work. – Aleksei Matiushkin Oct 29 '16 at 07:52
  • @mudasobwa yeah, it took me way too long to realize what was wrong with my code. If `adapter` is a struct, you can even do `adapter.__struct__`, but in this case it's not. – Dogbert Oct 29 '16 at 07:54
  • OK, so plain old good `case` instead of nifty pattern matching? It could be placed inside `defimpl App.AppRequest, for: Atom` though. – Aleksei Matiushkin Oct 29 '16 at 07:55
  • @mudasobwa `App.AppRequest.request Code.eval_string("%#{adapter}{}"), params` resulted `** (CompileError) nofile:1: Plug.Adapters.Test.Conn.__struct__/1 is undefined, cannot expand struct Plug.Adapters.Test.Conn` – ardhitama Oct 29 '16 at 07:56
  • @ardhitama would it be harmful to make a `Plug.Adapters.Test.Conn` a struct? E.g. simply put `defstruct dummy: nil` inside it? – Aleksei Matiushkin Oct 29 '16 at 07:58
  • @mudasobwa will be a disaster in deployment I guess – ardhitama Oct 29 '16 at 07:59
  • @ardhitama why making smth a dummy struct would somehow affect deployment? I am not sure I follow. – Aleksei Matiushkin Oct 29 '16 at 08:01
  • @mudasobwa CMIIW, we are about to modify `/deps/plug/lib/plug/adapters/test/conn.ex`? – ardhitama Oct 29 '16 at 08:04
  • 1
    Ah, it is apparently not yours? Got it. OK. After all, I would give up with protocols and do pattern matching in `AppHelper`: `defmodule App.AppHelper do def verify(%Plug.Conn{adapter: {Plug.Conn, _}} = conn, params) ... def verify(%Plug.Conn{adapter: {Test.Conn, _}} = conn, params)`. The above is a pseudocode, of course. – Aleksei Matiushkin Oct 29 '16 at 08:08

0 Answers0