mercredi 15 avril 2015

Rails: does minitest use schema.rb to recreate the test schema?

I am trying to bring a legacy Rails system up more current standards but having problems getting the test database to reflect the state of schema.rb plus changes made via migrations.


tl;dr Does running rake minitest:all invoke the same code as rake db:schema:load?


Environment



  • Rails 3.2.20

  • Ruby 1.9.3

  • Minitest gem 4.6.2

  • minitest-rails 0.5.2

  • MySQL 5.1

  • Local OS X, Test and Production Linux


Original System State


The system was originally set up by people who were not software engineers, didn't know Rails, etc. Several MySQL-specific types were added (e.g. an unsigned int) that are not specifically supported using Rails' migrations and schema.rb. So the system used the structure.sql and was very sloppy about how it was kept updated, checked in to git, and so on.


Further, at some point later, someone decided to replace some of the usual numeric, auto-incrementing primary key id fields with a varchar containing a self-generated GUID. The field name was still id but it was a new datatype.


But all of the hundreds of tests (they did write a lot of tests) had been written to reference fixture instances based on ids, not fixture names and there were dependencies between fixtures, etc. Rather than updating the tests, they decided to stick with the old schema (with numeric id) for tests, and use the new one (with varchar GUIDs in id) for production.


OP takes a deep breath...


Databases for various environments were out of sync, there were hundreds of migrations, but they stopped working because the "development" database was shared and ... well you know, it was a mess.


I am trying to fix all of this, and eventually move to PostgreSQL.


What I Have Done


I dumped the schema from the production database locally using RAILS_ENV=production rake db:structure:dump -- this produced an authoritative structure.sql. I created a fresh development database, and loaded its schema using rake db:structure:load -- I carefully compared production and development schemas and they were the same, even the MySQL-specific unsigned int I mentioned above.


I would like to move to using schema.rb for two reasons. First, I would like to get to a system that does not depend on MySQL. Second, when using structure.sql we check in a file that has the auto-increment values, DB settings and other characteristics of whatever machine ran the most recent db:migrate. This can create issues I would prefer to avoid.


So, I locally changed the configuration setting from :sqlto use the :ruby setting to produce schema.rb.


But schema.rb really doesn't like the idea of the id field being turned into a varchar -- I have made sure all of the models that use this declare self.primary_key = :id, then I created a new migration to replace all of the old ones, a "rollup migration", whose content is this mainly new schema.rb, but modified in several ways.


In particular, where the id field is the GUID varchar I set up the table like this (from within a migration):



class RolledUpStateAsOf20150403 < ActiveRecord::Migration

def up

# ... all other table definitions in the system

create_table "users", :id => false, :force => false do |t|
t.string "id", :limit => 36, :default => "", :null => false
t.string "login"
# and all the other user fields
end
execute("ALTER TABLE users ADD PRIMARY KEY (id);")

#...
end

def down
raise ActiveRecord::IrreversibleMigration
end
end


So:



  • :id => false to prevent migration from creating the normal id

  • :force => false to make sure this migration doesn't nuke the production DB

  • t.string "id" with size and other primary-key-like settings

  • execute(ALTER TABLE ...) at the end to declare the id as a primary key


Every time I create a new migration in the future, schema.rb will be updated -- it won't be accurate for now (until we get rid of those wacky id fields later). Migrations will honor normal Rails practices moving forward.


Once the production, staging, development and other databases are in sync then all is well.


Except when we test.


So Why Isn't this Working when I run Minitest?


I am running tests with minitest as follows:



RAILS_ENV=test rake db:drop
RAILS_ENV=test rake db:create
RAILS_ENV=test rake db:migrate
RAILS_ENV=test rake minitest:all


But then I start seeing errors, and the errors are due to the id columns being defined as int rather than varchar.


If I inspect the schema before running minitest (after drop, create, and my magic migration), it is correct: the funky primary keys are varchar as desired.


But somewhere during the test it looks like the schema is getting changed back to the Rails standard. I can go back and inspect the schema the ID columns are back to int.


Presumably the schema is being produced from the actual schema.rb.


Is this normal/expected behavior? Any suggestions about how to achieve my goals, which are simply:



  • Migrations work again

  • Development, test, staging, production databases are structurally the same

  • I need to live with the crazy varchar GUID-based id fields for now

  • I prefer not to use structure.sql if possible


Aucun commentaire:

Enregistrer un commentaire