kanndide
kanndide

Reputation: 13

SQLite3::ConstraintException: UNIQUE constraint failed is not saving object with errors, just throwing an error and stopping my tests

I am attempting to test returning errors on a non-unique attempt at creating a user in this API.

RSpec.describe 'POST /signup', type: :request do
  let(:url) { '/signup' }
  let(:params) do
    {
      user: {
      email: '[email protected]',
      password: 'password'
    }
  }
end

context 'when user is unauthenticated' do
    before { post url, params: params }

    it 'returns 200' do
      expect(response.status).to eq 200
    end

    it 'returns a new user' do
      expect(response).to match_response_schema('user')
    end
end

  context 'when user already exists' do
    before do
      Fabricate :user, email: params[:user][:email]
      post url, params: params
    end

    it 'returns bad request status' do
      expect(response.status).to eq 400
    end

   it 'returns validation errors' do
      expect(json['errors'].first['title']).to eq('Bad Request')
   end
 end

end

Above is my spec file. Below is the registration file that is throwing the error:

class RegistrationsController < Devise::RegistrationsController
  respond_to :json

  def create
     build_resource(sign_up_params)
     resource.save
     render_resource(resource)
  end
end

The error that I am getting:

3) POST /signup when user already exists returns validation errors
 Failure/Error: resource.save

 ActiveRecord::RecordNotUnique:
   SQLite3::ConstraintException: UNIQUE constraint failed: users.email: 
 INSERT INTO "users" ("email", "encrypted_password") VALUES (?, ?)

I know that it is going to throw that error, that's the point I've defined the following in my application controller to return the correctly formatted error:

class ApplicationController < ActionController::API

    def render_resource(resource)
        if resource.errors.empty?
          render json: resource
        else
          validation_error(resource)
        end
    end

  def validation_error(resource)
    render json: {
      errors: [
        {
          status: '400',
          title: 'Bad Request',
          detail: resource.errors,
          code: '100'
        }
      ]
     }, status: :bad_request
  end

end

The goal is that if resource.save doesn't save, or throws and error that the render_resource function will return a JSON formatted error. However, every time it hits the resource.save it throws the error in my test and stops the rest of the test. Any help would be greatly appreciated!

Upvotes: 1

Views: 721

Answers (1)

spickermann
spickermann

Reputation: 106792

save returns false and populates errors when a record wasn't saved to the database because of failing validations. In your case, there aren't failing any validations but the database raised an exception. These kinds of errors aren't handled in your code.

A unique index in the database is important to really ensure that you do not end up with duplicates in your database, but you still should add a validation to your model to show nice error messages to the user.

# add to app/models/user.rb
validates :email, uniqueness: { case_sensitive: false }

Read more about this type of validation in the Rails Guides.

Upvotes: 1

Related Questions