2016-07-01 7 views
12

Ich habe einen Authentifizierungsstecker und möchte meine Controller testen. Das Problem ist, dass die Leitung in diesem PlugWie man Verbindung mit Sitzung in Phoenix benutzt?

user_id = get_session(conn, :user_id) 

hat Und es ist immer Null, wenn ich diese Methode bin mit (I verwendet schmutzigen Hack vor, aber ich möchte nicht mehr, es zu tun):

@session Plug.Session.init([ 
    store:   :cookie, 
    key:    "_app", 
    encryption_salt: "secret", 
    signing_salt:  "secret", 
    encrypt:   false 
    ]) 

user = MyApp.Factory.create(:user) 

conn() 
|> put_req_header("accept", "application/vnd.api+json") 
|> put_req_header("content-type", "application/vnd.api+json") 
|> Map.put(:secret_key_base, String.duplicate("abcdefgh", 8)) 
|> Plug.Session.call(@session) 
|> fetch_session 
|> put_session(:user_id, user.id) 

Ich sende eine Patch-Anfrage mit dieser Verbindung, und ihre Sitzung user_id ist Null. Ergebnisse IO.puts conn in meinem Stecker:

%Plug.Conn{adapter: {Plug.Adapters.Test.Conn, :...}, assigns: %{}, 
before_send: [#Function<0.111117999/1 in Plug.Session.before_send/2>, 
    #Function<0.110103833/1 in JaSerializer.ContentTypeNegotiation.set_content_type/2>, 
    #Function<1.55011211/1 in Plug.Logger.call/2>, 
    #Function<0.111117999/1 in Plug.Session.before_send/2>], body_params: %{}, 
cookies: %{}, halted: false, host: "www.example.com", method: "PATCH", 
owner: #PID<0.349.0>, 
params: %{"data" => %{"attributes" => %{"action" => "start"}}, "id" => "245"}, 
path_info: ["api", "tasks", "245"], peer: {{127, 0, 0, 1}, 111317}, port: 80, 
private: %{MyApp.Router => {[], %{}}, :phoenix_endpoint => MyApp.Endpoint, 
    :phoenix_format => "json-api", :phoenix_pipelines => [:api], 
    :phoenix_recycled => true, 
    :phoenix_route => #Function<4.15522358/1 in MyApp.Router.match_route/4>, 
    :phoenix_router => MyApp.Router, :plug_session => %{}, 
    :plug_session_fetch => :done, :plug_session_info => :write, 
    :plug_skip_csrf_protection => true}, query_params: %{}, query_string: "", 
remote_ip: {127, 0, 0, 1}, req_cookies: %{}, 
req_headers: [{"accept", "application/vnd.api+json"}, 
    {"content-type", "application/vnd.api+json"}], request_path: "/api/tasks/245", 
resp_body: nil, resp_cookies: %{}, 
resp_headers: [{"cache-control", "max-age=0, private, must-revalidate"}, 
    {"x-request-id", "d00tun3s9d7fo2ah2klnhafvt3ks4pbj"}], scheme: :http, 
script_name: [], 
secret_key_base: "npvJ1fWodIYzJ2eNnJmC5b1LecCTsveK4/mj7akuBaLdeAr2KGH4gwohwHsz8Ony", 
state: :unset, status: nil} 

Was muss ich tun, um dieses Problem und Test-Authentifizierung gut zu lösen?

UPDATE Authentifizierung

defmodule MyApp.Plug.Authenticate do 
    import Plug.Conn 
    import Phoenix.Controller 

    def init(default), do: default 

    def call(conn, _) do 
    IO.puts inspect get_session(conn, :user_id) 
    IO.puts conn 
    user_id = get_session(conn, :user_id) 

    if user_id do 
     current_user = MyApp.Repo.get(MyApp.Task, user_id) 
     assign(conn, :current_user, current_user) 
    else 
     conn 
     |> put_status(401) 
     |> json(%{}) 
     |> halt 
    end 
    end 
end 

Router Stecker (I zerschnitt einige Teile von hier):

defmodule MyApp.Router do 
    use MyApp.Web, :router 

    pipeline :api do 
    plug :accepts, ["json-api"] # this line and 3 below are under JaSerializer package responsibility 
    plug JaSerializer.ContentTypeNegotiation 
    plug JaSerializer.Deserializer 
    plug :fetch_session 
    plug MyApp.Plug.Authenticate # this one 
    end 

    scope "/api", MyApp do 
    pipe_through :api 

    # tasks 
    resources "/tasks", TaskController, only: [:show, :update] 
    end 
end 
+0

(Um 'conn' zu drucken, benutzen Sie' IO.inspect conn'.) – Dogbert

+0

@Dogbert danke ich weiß :) Wie man mein Problem löst? – asiniy

+0

Können Sie Ihren vollständigen Authentifizierungsstecker posten? – TheAnh

Antwort

2

es viel einfacher gelöst werden können durch Sitzungen insgesamt in Tests zu umgehen. Die Idee ist, current_user direkt an die Verbindung in Tests und in der Authentifizierung Stecker zuweisen - überspringen Abrufen von Benutzer aus der Sitzung, wenn die current_user Zuweisung festgelegt ist. Dies lässt natürlich den Authentifizierungsstecker selbst ungeprüft, aber das Testen sollte dort viel einfacher sein, als den gesamten Stapel zu durchlaufen.

# in the authentication plug 
def call(%{assigns: %{current_user: user}} = conn, opts) when user != nil do 
    conn 
end 
def call(conn, opts) do 
    # handle fetching user from session 
end 

Auf diese Weise können Sie nur assign(conn, :current_user, user) in Tests tun, um die Verbindung zu authentifizieren.

+0

Ich denke nicht, dass es eine gute Idee ist. Ich habe diese Lösung übrigens schon gesehen. – asiniy

+0

Ich würde sagen, das ist der Standardweg, um das zu lösen, den Sie an den meisten Orten finden werden. Ich würde erwarten, die Sitzung direkt zu manipulieren, da dies möglicherweise nicht wie erwartet funktioniert, da Sitzungen normalerweise eine Hin- und Rückfahrt zum Server und zurück beinhalten, um wirksam zu werden. – michalmuskala

1

Da Sie Ihre Sitzung vor fetch_session/2 so in der Authentifizierungs-Plug rufen get_session/2 kehrt nil

Lassen Sie uns Ihre Authentifizierungs-Plug ändern, um einen Test zu machen:

defmodule MyApp.Plug.Authenticate do 
    import Plug.Conn 
    import Phoenix.Controller 
    alias MyApp.{Repo, User} 

    def init(opts), do: opts 

    def call(conn, _opts) do 
    if user = get_user(conn) do 
     assign(conn, :current_user, user) 
    else 
     conn 
     |> put_status(401) 
     |> put_flash(:error, "You must be logged in!") 
     |> halt 
    end 
    end 

    def get_user(conn) do 
    case conn.assigns[:current_user] do 
     nil -> 
     case get_session(conn, :user_id) do 
      id -> fetch_user(id) 
      nil -> nil 
     end 
     user -> user 
    end 
    end 

    defp fetch_user(id), do: Repo.get!(User, id) 
end 

Jetzt können Sie Ihre Stecker wie folgt testen:

defmodule MyApp.Plug.AuthenticateTest do 
    use ExUnit.Case, async: true 
    use Plug.Test 
    import Phoenix.ConnTest 
    alias MyApp.Plug.Authenticate 

    @endpoint MyApp.Endpoint 

    @session Plug.Session.init([ 
    store:   :cookie, 
    key:    "_app", 
    encryption_salt: "secret", 
    signing_salt:  "secret", 
    encrypt:   false 
    ]) 

    setup do 
    user = MyApp.Factory.create(:user) 

    conn = build_conn() 
    |> put_req_header("accept", "application/vnd.api+json") 
    |> put_req_header("content-type", "application/vnd.api+json") 
    |> Map.put(:secret_key_base, String.duplicate("abcdefgh", 8)) 
    |> Plug.Session.call(@session) 
    |> fetch_session 
    |> put_session(:user_id, user.id) 

    {:ok, conn: conn, user: user} 
    end 

    test "get_user returns where it is set in session", %{conn: conn, user: user} do 
    assert Authenticate.get_user(conn) == user 
    end 
end 

Und schließlich können Sie Ihren Controller testen wie:

setup do 
    user = MyApp.Factory.create(:user) 

    {:ok, user: user} 
    end 

    test "GET /login", %{user: user} do 
    conn = build_conn() 
    |> assign(:current_user, user) 
    |> get("/login") 

    assert html_response(conn, 200) =~ "Successfull login" 
    end 

Es gibt eine ähnliche Frage wie folgt aus:

how can i set session in setup when i test phoenix action which need user_id in session?

Und eine bessere Art und Weise haben, wenn Sie die Benutzer wollen für einen Test injizieren ist es speichern in conn.private und es aus privaten lesen Ihr Authentifizierungsstecker. Sie sollten sich die Änderung ansehen. Hoffe, dass Sie helfen!