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 :sql
to 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 DBt.string "id"
with size and other primary-key-like settingsexecute(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-basedid
fields for now - I prefer not to use
structure.sql
if possible
Aucun commentaire:
Enregistrer un commentaire