Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby on Rails deleting fixtures with foreign keys

I'm having trouble setting up the tests with fixtures that use foreign keys! And I would be much appreciated if someone could help me understand this.

Let's say for example the :user_type model has a reference to the :role model, when tests get executed, and all the data in the test db is deleted to be again re-inserted, Rails deletes the data from the role model first, instead of deleting the data first from the :user_type and only then from :role.

The fixtures:

#roles.yml
technical:
  name: 'Technical'
  obs: 'User Role for technical / maintenance users!'


#user_types.yml
technic:
  role: technical
  name: 'Technic'
  is_admin: true

The tests:

#app/test/models/role_test.rb
require 'test_helper'

class RoleTest < ActiveSupport::TestCase
  fixtures :roles

  test 'save Role without name' do
    data = Role.new()
    data.valid?
    assert data.errors.added?(:name, :blank), 'Role saved without name!'
  end
end


#app/test/models/user_type_test.rb
require 'test_helper'

class UserTypeTest < ActiveSupport::TestCase
  fixtures :user_types

  test 'save User Type without role_id' do
    data = UserType.new(:name => 'public')
    data.valid?
    assert data.errors.added?(:role_id, :blank), 'User Type saved without role'
  end
end

When the tests ran for the first time, everything goes ok. Rails cleans the database (in this point is still empty, so there is no constraint violations), then the data from the fixtures gets inserted, and the tests run fine. The next time I try to run the tests they will fail because when rails starts to delete the data from the database, it starts with the role model/table instead of the user_type! Because foreign keys where defined on these models a violation will occur because user_type is still referring to data in the model table!

How should this be done properly? Is there any mechanism to tell rails the order to destroy the fixture data? I am using RubyOnRails 4 and Firebird 2.5 by the way.

I have been struggling with this for a few days and I haven’t been able to do it right!

Thank you in advance!

like image 565
CrazyMenConnected Avatar asked Jun 09 '15 10:06

CrazyMenConnected


1 Answers

I encountered a similar problem, but I use PostgreSQL. I am posting my solution with the hope that an analogous solution exists for Firebird (which I have not personally used).

As you mentioned, when Rails executes the test suite, it starts by deleting all of the data in the database tables. This is tricky in the presence of foreign key constraints. In theory, one way to handle these constraints would be to figure out an appropriate order in which to delete records.

However, at least for PostgreSQL, Rails actually sidesteps the issue by temporarily disabling the triggers which would otherwise prevent violation of these foreign key constraints. It does this with the following (probably vendor-specific) pair of SQL commands, executed before and after the DELETE commands:

ALTER TABLE tablename DISABLE TRIGGER ALL
ALTER TABLE tablename ENABLE TRIGGER ALL

There's a catch: only a superuser role (where "role" basically means "user" in this context) can execute these commands. Effectively, Rails can't run tests unless it is using a PostgreSQL role with the superuser privilege. Maybe Firebird has superusers, too?

Side note for PostgreSQL users with this problem:

To grant superuser to a user (only do this on your test or development database)

Run this SQL statement: ALTER USER myuser WITH SUPERUSER;

This is the error message that appears as of Rails 4.2.4 when attempting to test without superuser privilege:

ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: update or delete on table "tablename" violates foreign key constraint

In a future version of Rails, this more specific error message will be shown:

WARNING: Rails was not able to disable referential integrity. This is most likely caused due to missing permissions. Rails needs superuser privileges to disable referential integrity.

See rails/rails commit 72c1557 and PR #17726 for more details.

like image 74
liverwust Avatar answered Oct 20 '22 12:10

liverwust