PG::UndefinedTable: ERROR: relation does not exist - ruby-on-rails

I'm getting errors like this:
Failure/Error: let(:slide) { build :slide }
ActiveRecord::StatementInvalid:
PG::UndefinedTable: ERROR: relation "albums" does not exist at character 13
: INSERT INTO "albums" ("created_at", "name", "updated_at", "user_id") VALUES ($1, $2, $3, $4) RETURNING "id"
in some random way - sometimes all passes successfully and sometimes some of them don't. I'm getting those usually for same set of tests but when I run only them then I can't reproduce the problem.
I just moved to Postgres as I started to use gem devise and gem apartment for multi-tenancy which works best with schemas. Since then I added to my tests around hook to simulate user login:
given(:user) { create :user }
around :each do |scenario|
login_and_switch_schema user
scenario.run
destroy_users_schema user
destroy_user user
end
all above helpers are defined at spec/support/auth_helpers.rb.
I'm not sure what else is important to dig into it so I just made a commit to github here.
I'm testing it with:
rake db:test:prepare && rspec -X ./spec/models/ ./spec/controllers/ ./spec/features/
(turning off spork doesn't help).
Any ideas ? I feel dizzy looking at it without a clue what to do. Thanks in advance.
UPDATE:
I don't get those errors anymore after changing around hook for every spec file to:
before(:all) do
#user = create :user
end
after(:all) do
destroy_users_schema #user
destroy_user #user
end
before(:each) do
login_and_switch_schema #user
end
at the same time it works twice as fast. Anyway this is more like a workaround as root cause still stays as puzzle for me.

Related

RSpec proper way to check created column

I've got POST endpoint which creates a new JourneyProgress record in my db.
post :enroll do
JourneyProgress.create!(user: current_user, journey: journey, percent_progress: 0.0, started_at: DateTime.now)
status :no_content
end
I want to check if percent_progress and started_at were set with below example:
let(:current_date) { 'Thu, 16 Jul 2020 17:08:02 +0200'.to_date }
before do
allow(DateTime).to receive(:now) { current_date }
end
it 'set starting progress' do
call
expect(JourneyProgress.last.started_at).to eq(current_date)
expect(JourneyProgress.last.percent_progress).to eq(0.0)
end
The specs will pass but I'm not sure if JourneyProgress.last.(some record name) is in line with convention. Is there a better way to check this?
If I change it to:
it 'set starting progress' do
expect(call.started_at).to eq(current_date)
...
end
I'm getting an error:
NoMethodError:
undefined method `started_at' for 204:Integer
If you really want to test the value of the started_at column, something like this would work.
it 'set starting progress' do
call
expect(JourneyProgress.last.started_at).to eq(current_date)
end
but I'd suggest that you think twice about what is worth testing in this scenario, it would make a lot more sense to check that a JourneyProgress record is being inserted into your DB and that the endpoint actually returns the correct HTTP Status code.
it 'persists the record in database' do
expect { call }.to change { JourneyProgress.count }.by(1)
end
it 'responds with no content status' do
call
expect(response).to have_http_status(:no_content)
end
As other comments state I'd also use 201 (created) instead of no content in this context.
It looks like you are trying to write an integration test that verifies that your software wrote a record to the database with the appropriate values for percent_progress and started_at.
You are correct to be concerned about using .last in your tests like you have. If you were to run your tests in parallel on a build server, there's a good chance that two different tests would both be adding records to the database at the same time (or in an indeterminate order), causing the test to be flaky. You could resolve this potential flakiness by returning the id of the newly created record and then looking up that record in the test after the call event. But, there's a better solution...
If you were to modify your migration for the JourneyProgress model to look like this:
create_table :journey_progress do |t|
t.user_id :integer
t.journey_id :integer
t.percent_progress :float, default: 0.0
t.timestamps
end
Then, you would be guaranteed that the percent_progress field would always default to 0.0. And, you could use the ActiveRecord managed created_at timestamp in lieu of the custom started_at timestamp that you would have to manage.
So, you won't have to test that either thing got set correctly, because you can trust ActiveRecord and your database to do the right thing because they've already been thoroughly tested by their authors.
Now, your code would look more like this:
post :enroll do
journey_progress = JourneyProgress.create!(
user: current_user,
journey: journey
)
status :created
end
And, your tests would look more like what Sebastian Delgado mentioned:
it 'persists the record in database' do
expect { call }.to change { JourneyProgress.count }.by(1)
end

RoR : integration test fail

I have 2 integrations tests, here how it's look :
class TagFlowTest < ActionDispatch::IntegrationTest
include Devise::Test::IntegrationHelpers
setup do
sign_in FactoryGirl.create(:admin)
#partner = create(:partner)
end
test 'can see the list of partners' do
get '/partners'
assert_response :success
assert_select 'tr', 2
end
... more tests below ...
The second test looks the same
The thing is that when a launch rails test on this test, it's working fine. But when I launch :
rails test /test/
I got an error :
UnexpectedError: ActiveRecord::RecordNotUnique: PG::UniqueViolation:
ERROR: duplicate key value violates unique constraint
"index_users_on_email"
I understand that the issue come from
sign_in FactoryGirl.create(:admin)
When I delete this line on the other test, it's working.
But if I do that, I cannot test one test only. How can I do the resolve this ?
UnexpectedError: ActiveRecord::RecordNotUnique: PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "index_users_on_email"
So what does this error mean? You have some validation in the model level or the db level which will not let you have the duplicate email for Admin.
So I presume that the factory you used to create Admin, is not creating unique email addresses.
Try this
FactoryGirl.define do
factory :admin do
# your code
email { Faker::Internet.email }
end
end
But this thing requires faker gem. If you don't want to use a gem just for this, try using sequences in factory girl.
Also it's good to have the database in a clean state, when running tests. Consider using https://github.com/DatabaseCleaner/database_cleaner

Rails test error

I am very new to testing in Rails. I am attempting to create a very basic test for the index action in my albums controller. I am getting an error, not jsut on this test, but on all of my tests. The error looks like this:
bin/rails test test/controllers/albums_controller_test.rb:18
E
Error:
AlbumsControllerTest#test_should_get_edit:
ActiveRecord::RecordNotUnique: SQLite3::ConstraintException: UNIQUE constraintfailed: users.email: INSERT INTO "users" ("created_at", "updated_at", "id") VALUES ('2017-01-11 21:54:05.906006', '2017-01-11 21:54:05.906006', 298486374)
All of my tests get this error, not just this one. This is the test I was attempting to run with the above example:
require 'test_helper'
class AlbumTest < ActiveSupport::TestCase
test "index action should work" do
get :index
assert_response :success
end
end
Here is the index action in my albums controller:
def index
#albums = Album.all.order(year: :desc).order(title: :asc)
end
Not sure what's going on. Help would be appreciated!
I would need some more code to give a better solution, but by the error it seems that you are trying to create more than one user with the same email. And since you are validating them to be UNIQUE, than the test fails.
So I think the problem must be somewhere else where you create the users for the test.
If you're using fixtures, you can try something like this:
john:
name: $LABEL
email: $LABEL#example.com
And if you are using factory girl, you can try something like this:
factory :user do
name "John"
sequence(:email) { |n| "email#{n}#example.com" }
Hope it helps..

Rails 3 controller test

I'm writing an app with Rails 3. In my functional test, test/functional/cust_infos_controller_test.rb, I have these:
require 'test_helper'
class CustInfosControllerTest < ActionController::TestCase
# Replace this with your real tests.
test "should get cust" do
get :customerinfo
assert_response :success
assert_not_nil assigns("cust_infos")
end
end
My controller is fairly straightforward as it just finds all the customer's info:
class CustInfosController < ApplicationController
def customerinfo
#cust_info = CustInfo.find(:all, :order=>"cust_id")
#cust_info.each do |ci|
if ci.upload_freq == 'W'
ci.upload_freq = 'Weekly'
elsif ci.upload_freq == 'M'
ci.upload_freq = 'Monthly'
end
end
end
end
When I ran it with:
$ ruby -Itest test/functional/cust_infos_controller_test.rb
I got the following error:
Loaded suite test/functional/cust_infos_controller_test
Started
E
Finished in 0.025258 seconds.
1) Error:
test_should_get_cust(CustInfosControllerTest):
ActiveRecord::StatementInvalid: PGError: ERROR: numeric field overflow
DETAIL: A field with precision 4, scale 0 must round to an absolute value less than 10^4.
: INSERT INTO "cust_infos" ("cust_id") VALUES (298486374)
1 tests, 0 assertions, 0 failures, 1 errors
In my cust_infos table, I have cust_id as integer. But I don't know why when I ran controller test to just get some record, active record will execute insert statement. How do I fix this?
UPDATE: I commented out all the lines in customerinfo method, and run the same test. Got the exact same result. So I'm guessing it's not my methods behavior, but rather Rails? Any hint?
I see what you are trying to do. What you need to do is create a helper for your view.
application_helper.rb
def display_upload_freq(s)
case s
when 'W'
'Weekly'
when 'M'
'Monthly'
end
end
cust_info_controller.rb
class CustInfosController < ApplicationController
def customerinfo
#cust_info = CustInfo.find(:all, :order=>"cust_id")
end
end
Then in your view when your are iterating through #cust_info, for upload_freq, use dislay_upload_freq(ci.upload_freq). Assuming you have #cust_info.each do |ci|. Then you won't be confusing the db with saving anything.
Are you using factories or fixtures to create your test data? If not, how are you creating it? My guess is that the insert is happening when your test data is being set up, not because of anything happening in your controller. The second part of that guess is borne out by the fact that commenting out all code in your controller isn't getting rid of the error.
I know this post is quite old, but I will post my answer in case someone is brought here by search engines.
I had the exact same problem, the issue originates from the fixture file(s) in test/fixtures/{name_of_your_model}.yml. Rails adds some initial values in this file, in my case it looked like this:
one: {}
# column: value
#
two: {}
# column: value
and when you want to run the test, it will try to create the test database using this fixtures. And that's where the issue occurs. Trying to create an empty record while you did not allow a null ID in your table.
Deleting these fixtures, or filling them with appropriate values should solve the problem.

Does rails do a rollback if I use begin...rescue?

I'd like to add a begin...rescue block to one of my controllers create method, in order to log better info and construct the correct error message to return to the client. Does the rescue in any way 'interrupt' the rollback process?
I'm assuming rails automatically does a rollback. When does it happen? Has it already happened by the time I get in the rescue clause?
I'm using mySQL on Dreamhost and I think they use innoDB.
I've been experimenting with this. It seems like if your rescue catches the exception that would have caused the rollback, the part of the transaction that already happened gets committed. In my case, I want the database rolled back to the way it was before the transaction started, but I still want to handle the exception.
I ended up with this:
self.transaction do
first_operation
begin
operation_that_might_violate_db_constraint
rescue ActiveRecord::RecordNotUnique
#deal with the error
raise ActiveRecord::Rollback #force a rollback
end
end
The raise ActiveRecord::Rollback part makes sure the transaction gets completely rolled back. Without it, the changes from first_operation would end up getting committed.
The ActiveRecord::Rollback is a special kind of exception that doesn't bubble above the level of the transaction, so you won't end up with an uncaught exception that renders the error page.
I'm not sure this is the gold-standard way of doing this, but it seems to work.
Just using begin...rescue isn't enough to rollback a transaction. You need to use:
ModelName.transaction do
end
This is done explicitely on a call to save, so that all of your callbacks are executed together. What exceptions are you catching in your rescue block? What are you responding to? What kind of errors?
Rollback not be processed.
ex:
create_table "helps", :force => true do |t|
t.string "title", :null => false
t.text "content"
end
#Rails console
Help.transaction do
Help.create! title: "aaa"
begin
Help.create! content: "111"
rescue
p "create error."
end
Help.create! title: "bbb"
end
#get this
>> "create error."
Help.count
>> 2
You can also try my answer for rollback, catch and rendering for your create method using ActiveRecord::Base.transaction:-
Click Here
Thanks

Resources