2016-07-01 13 views
12

Mam wtyczkę uwierzytelniania i chcę przetestować moje kontrolery. Problem polega na tym, że linia w tej wtyczce maJak korzystać z połączenia z sesją w Phoenix?

user_id = get_session(conn, :user_id) 

i to zawsze występuje gdy używam tej metody (użyłem brudną siekać wcześniej, ale ja już nie chcę tego robić):

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

Wysyłam żądanie poprawki przy użyciu tego połączenia, a jego sesja id_użytkownika jest zerowa. Wyniki IO.puts conn w moim wtyczki:

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

Co muszę zrobić, aby rozwiązać ten problem i uwierzytelnianie testowy dobrze?

UPDATE Authentication podłączyć

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 

routera (I skróconych niektóre części stąd):

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

(Aby wydrukować 'conn', użyj' IO.inspect conn'.) – Dogbert

+0

@Dogbert dzięki Wiem :) Jak rozwiązać mój problem? – asiniy

+0

Czy możesz opublikować swoją pełną wtyczkę uwierzytelniającą? – TheAnh

Odpowiedz

2

To może być rozwiązany znacznie łatwiejsze, pomijając całkowicie sesje w testach. Chodzi o to, aby przypisać current_user bezpośrednio do połączenia w testach i wtyczce uwierzytelniającej - pomiń pobieranie użytkownika z sesji, gdy ustawione jest przypisanie current_user. To oczywiście pozostawia niezmienioną wtyczkę uwierzytelniającą, ale testowanie jej powinno być znacznie łatwiejsze niż przejście przez cały stos.

# 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 

Umożliwia to po prostu wykonanie assign(conn, :current_user, user) w testach służących do uwierzytelnienia połączenia.

+0

Nie sądzę, że to dobry pomysł. Już to widziałem. – asiniy

+0

Powiedziałbym, że to standardowy sposób rozwiązania tego problemu, który można znaleźć w większości miejsc. Spodziewam się, że sesja manipulacyjna bezpośrednio w ten sposób może nie działać zgodnie z oczekiwaniami, ponieważ sesje zwykle obejmują objazd na serwer i powrót do skutku. – michalmuskala

1

Od zadzwonić sesji przed fetch_session/2 więc na wtyczce uwierzytelniania get_session/2 powróci nil

Zmieńmy swoje wtyczki uwierzytelniania zrobić 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 

Teraz możesz przetestować swoją wtyczkę tak:

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 

I na koniec można przetestować kontroler jak:

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 

Jest podobne pytanie tak:

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

A masz lepszy sposób, gdy chcemy użytkownikowi wstrzyknąć do testu jest przechowywać go w conn.private i czytać ją od prywatnych twoja wtyczka uwierzytelniająca. Powinieneś rzucić okiem, aby zobaczyć zmianę. Mam nadzieję, że ci pomogę!