Aún no tienes acceso a esta clase

Crea una cuenta y continúa viendo este curso

Autenticación: Finalizando pruebas de creación y actualización de blogposts

25/33
Recursos

Aportes 10

Preguntas 2

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.

Estuve un buen rato tratando de encontrar por qué me estaba fallando una prueba con el error:

Failure/Error: it { is_expected.to have_http_status(:unauthorized)}
       expected the response to have status code :unauthorized (401) but it was :internal_server_error (500)

Y tenía mal escrita la palabra unauthorized en un status. Eso me hizo dar cuenta que esas palabras no las elige Simon aleatoriamente sino que corresponde a un http status code así que decidí buscar el listado de simbolos rails para esos status y aquí está el listado:
http://billpatrianakos.me/blog/2013/10/13/list-of-rails-status-code-symbols/

Como una pequeña critica, podria decirte que cuando borraas y scrolleas en el codigo, esperes aunque sea 2 segundos para hacerlo, es muy dificil seguirte el paso si vas a una velocidad y es bastante molesto tener que estar pausando constantemente el video.

Finished in 1.99 seconds (files took 2 seconds to load)
13 examples, 0 failures
Finished in 0.70052 seconds (files took 0.95774 seconds to load)
22 examples, 0 failures

El codigo para autenticacion hasta este punto deja igual acceder a la accion del controlador en el caso de que header no este presente, y da error cuando llamamos Current.user, mi codigo es el siguiente:

def authenticate_user!
    # authenticate header
    headers = request.headers
    token_regex = /^Bearer (\w+)/

    if headers[:authorization].present? && headers[:Authorization].match(token_regex)
      token = headers[:Authorization].match(token_regex)[1]
      Current.user = User.find_by(auth_token: token)
    end

    render json: { error: "Unauthorized" }, status: :unauthorized if Current.user.nil?
  end

Es una buena practica usar la tecnica de setup, act, validation en cada test, enacapsulando então cada situacion de forma que no sea necesario hacer scroll mientras se lee el test y aprovechando mejor el lazy lookup, pues se debe tener en cuenta que una parte muy importante de los test es que sirvan como documentación, entonces deben tener una legibilidade lo mas comoda y clara possible sin importar si repetimos codigo en varias partes del documento, pues, en el caso de los test documentar es prioritario.

Por exemplo, la siguiente seria una mejor version de private_post_spec.rb:

require 'rails_helper'

RSpec.describe 'Posts with authentication' do
  describe 'Get /posts/{id}' do
    context 'when requisting others author post' do
      context 'when post is public' do
	#setup
        let!(:user) { create(:user) }
        let!(:other_user_published_post) { create(:published_post) }
        let!(:auth_headers) { { 'Authorization' => "Bearer #{user.auth_token}" } }

	#act
        before { get "/posts/#{other_user_published_post.id}", headers: auth_headers }

        context 'payload' do
          subject { payload }
	#validation
          it { is_expected.to include(:id) }
        end

        context 'response' do
          subject { response }
	#validation
          it { is_expected.to have_http_status(200) }
        end
      end

      context 'when post is arquived' do
        let!(:user) { create(:user) }
        let!(:other_user_archived_post) { create(:archived_post) }
        let!(:auth_headers) { { 'Authorization' => "Bearer #{user.auth_token}" } }

        before { get "/posts/#{other_user_archived_post.id}", headers: auth_headers }
        
        context 'payload' do
          subject { payload }
          it { is_expected.to include(:error) }
        end

        context 'response' do
          subject { response }
          it { is_expected.to have_http_status(404) }
        end
      end
    end
  end

  describe 'POST /posts' do
    context 'with valid authentication' do
      let!(:user) { create(:user) }
      let!(:auth_headers) { { 'Authorization' => "Bearer #{user.auth_token}" } }
      let!(:create_params) { { 'post' => {'title' => 'title', 'content' => 'content', 'status' => 'published'} } }

      before { post "/posts", params: create_params, headers: auth_headers }
        
      context 'payload' do
        subject { payload }
        
        it { is_expected.to include(:id, :title, :content, :status, :author) }
      end

      context 'response' do
        subject { response }

        it { is_expected.to have_http_status(201) }
      end
    end

    context 'without authentication' do
      let!(:create_params) { { 'post' => {'title' => 'title', 'content' => 'content', 'status' => 'published'} } }

      before { post "/posts", params: create_params }
        
      context 'payload' do
        subject { payload }

        it { is_expected.to include(:error) }
      end

      context 'response' do
        subject { response }

        it { is_expected.to have_http_status(401) }
      end
    end
  end

  describe 'PUT /posts' do
    context 'with valid authentication' do
      context 'when updating users post' do
        let!(:user) { create(:user) }
        let!(:user_post) { create(:published_post, user_id: user.id) }
        let!(:auth_headers) { { 'Authorization' => "Bearer #{user.auth_token}" } }
        let!(:update_params) { { 'post' => {'title' => 'title', 'content' => 'content', 'status' => 'published'} } }

        before { put "/posts/#{user_post.id}", params: update_params, headers: auth_headers }
          
        context 'payload' do
          subject { payload }

          it { is_expected.to include(:id, :title, :content, :status, :author) }
          it { expect(payload[:id]).to eq(user_post.id) }
        end

        context 'response' do
          subject { response }

          it { is_expected.to have_http_status(200) }
        end
      end

      context 'when updating other users post' do
        let!(:user) { create(:user) }
        let!(:other_user_published_post) { create(:published_post) }
        let!(:auth_headers) { { 'Authorization' => "Bearer #{user.auth_token}" } }
        let!(:update_params) { { 'post' => {'title' => 'title', 'content' => 'content', 'status' => 'published'} } }

        before { put "/posts/#{other_user_published_post.id}", params: update_params, headers: auth_headers }
          
        context 'payload' do
          subject { payload }

          it { is_expected.to include(:error) }
        end

        context 'response' do
          subject { response }

          it { is_expected.to have_http_status(404) }
        end
      end
    end
  end

  def payload
    JSON.parse(response.body).with_indifferent_access
  end
end	

Concerns

El orden de

rescue_from

en posts_controller,rb importa. Uno debe escribir de la excepción más general a la más especifica, el último rescue_from tiene la mayor prioridad:
https://stackoverflow.com/questions/58634123/global-error-handling-and-the-order-of-included-rescue-from

Estimados al realizar las pruebas se presenta un problema con el parámetro :autor dentro del include, que creen que pueda realizar para solucionar este problema.

++Código de la prueba: ++

describe "3. PUT /posts" do
    # con auth -> 
      # actualizar un post nuestro
      # !actualizar un post de otro -> 401
    context "with valid auth" do
      context "when updating users's post" do
        before { put "/posts/#{user_post.id}", params: update_params, headers: auth_headers }
        context "payload" do
          subject { payload }
          #it { is_expected.to include(:id, :title, :content, :published, :author) }
          it { expect(payload).to include(:id, :title, :content, :published) }
          it { expect(payload[:id]).to eq(user_post.id) }
        end
        context "response" do
          subject { response }
          it { is_expected.to have_http_status(:ok) }
        end
      end

      context "when updating other users's post" do
        before { put "/posts/#{other_user_post.id}", params: update_params, headers: auth_headers }
        context "payload" do
          subject { payload }
          it { is_expected.to include(:error) }
        end
        context "response" do
          subject { response }
          it { is_expected.to have_http_status(:not_found) }
        end
      end
    end
  end

  private

  def payload
    JSON.parse(response.body).with_indifferent_access
    #JSON.parse(response.body)
  end

Mensaje de Error:

Failures:

  1) Posts with authentication 3. PUT /posts with valid auth when updating users's post payload is e
xpected to include :id, :title, :content, :published, and :author
     Failure/Error: it { expect(payload).to include(:id, :title, :content, :published, :author) }

       expected {"autor" => {"email" => "[email protected]", "id" => 1, "name" => "Herbert Rodriguez
"}, "content" => "content", "id" => 1, "published" => true, "title" => "title"} to include :author
       Diff:
       @@ -1,5 +1,9 @@
       -[:id, :title, :content, :published, :author]
       +"autor" => {"email"=>"[email protected]", "id"=>1, "name"=>"Herbert Rodriguez"},
       +"content" => "content",
       +"id" => 1,
       +"published" => true,
       +"title" => "title",

     # ./spec/requests/private_posts_spec.rb:94:in `block (6 levels) in <top (required)>'
     # ./spec/rails_helper.rb:59:in `block (3 levels) in <top (required)>'
     # ./spec/rails_helper.rb:58:in `block (2 levels) in <top (required)>'

Finished in 1.8 seconds (files took 4.19 seconds to load)
5 examples, 1 failure

Failed examples:

rspec ./spec/requests/private_posts_spec.rb:94 # Posts with authentication 3. PUT /posts with valid
auth when updating users's post payload is expected to include :id, :title, :content, :published, an
d :author

Quitando el parámetro :autor

.....

Finished in 2.17 seconds (files took 5.37 seconds to load)
5 examples, 0 failures

Hola les comparto un par de modificaciones por si a alguien le pasa, el inconveniente era que no le llegaban las cabeceras definidas y enviadas desde las pruebas a mi controlador.

Lo que me toco hacer fue lo siguiente:

En el controlador definir este metodo:

    def initialize(headers = {})
      @headers = headers
    end

eso al parecer lo que hizo fue permitir sobre escribir o hacer set al header del request.

Lo otro fue en el archivo secured.rb

dejar la funcion de esta manera:

module Secured
  def authenticate_user!
    #Bearer xxxxxxx
    token_regex = /Bearer (\w+)/
    #leer HEADER de Auth
    headers=request.headers
    #verificar que sea valido
    if headers['HTTP_AUTHORIZATION'].present? && headers['HTTP_AUTHORIZATION'].match(token_regex)
      token=headers['HTTP_AUTHORIZATION'].match(token_regex)[1]
      #debemos verificar que el token corresponda a un user
      if (Current.user=User.find_by_user_reset_password_token(token))
            return
      end
    end
    render json: {error:'Unauthorized'}, status: :unauthorized
  end
end

Quien sabe la causa científica de esto y el porque la codificación del curso no me funciono?

Gracias.