2016-07-01 17 views
12

Ho una presa di autenticazione e voglio testare i miei controller. Il problema è che la linea in questo plug haCome utilizzare la connessione con la sessione di phoenix?

user_id = get_session(conn, :user_id) 

Ed è sempre pari a zero quando sto usando questo metodo (io ho usato trucco sporco prima, ma non ho più voglia di farlo):

@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) 

Sto inviando una richiesta di patch usando questo conn, e la sua sessione user_id è nulla. I risultati di IO.puts conn nella mia spina:

%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} 

Cosa devo fare per risolvere bene questa autenticazione emissione e di prova?

UPDATE autenticazione spina

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 (ho tagliato alcune parti da qui):

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

(Per stampare 'conn', utilizzare' IO.inspect conn'.) – Dogbert

+0

@Dogbert grazie so :) Come risolvere il mio problema? – asiniy

+0

Puoi pubblicare la tua spina di autenticazione completa? – TheAnh

risposta

2

può essere risolto molto più facile bypassando sessioni complessivamente nei test. L'idea è di assegnare current_user direttamente al conn in test e nel plug-in di autenticazione - salta l'utente dalla sessione quando è impostato l'assegnamento current_user. Questo ovviamente lascia la test di autenticazione non testata, ma testare lì dovrebbe essere molto più semplice, che passare attraverso l'intero stack.

# 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 

Questo vi permette di fare solo assign(conn, :current_user, user) nei test per autenticare la connessione.

+0

Non penso sia una buona idea. A proposito, ho già visto questa soluzione. – asiniy

+0

Direi che è il modo standard per risolverlo che troverai nella maggior parte dei posti. Mi aspetterei che manipolare la sessione direttamente come questa potrebbe non funzionare come previsto, perché le sessioni di solito comportano un viaggio di andata e ritorno verso il server e tornare ad avere effetto. – michalmuskala

1

Dal momento che si chiama la sessione prima di fetch_session/2 così nella spina di autenticazione get_session/2 tornerà nil

Cambiamo la vostra spina di autenticazione per fare un test:

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 

Ora è possibile testare la vostra spina in questo modo:

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 

E infine è possibile testare il controller come:

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 

C'è una domanda simile come questo:

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

e hanno un modo migliore quando si desidera che l'utente iniettare per un test è conservarlo in conn.private e leggerlo da privato in la tua spina di autenticazione. Dovresti dare un'occhiata per vedere il cambiamento. Spero che ti aiuti!

Problemi correlati