summaryrefslogtreecommitdiff
path: root/doc/development/testing_guide/testing_migrations_guide.md
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-09-24 06:06:02 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-09-24 06:06:02 +0000
commit4a45a787703cb78c6101750cfbdc9f656b934b42 (patch)
treef75dfc23baed5f27be7799411b4ebb8c8bd20ceb /doc/development/testing_guide/testing_migrations_guide.md
parent83ad9ec8cc449dca0b57a34a10afd529326c1d57 (diff)
downloadgitlab-ce-4a45a787703cb78c6101750cfbdc9f656b934b42.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'doc/development/testing_guide/testing_migrations_guide.md')
-rw-r--r--doc/development/testing_guide/testing_migrations_guide.md167
1 files changed, 167 insertions, 0 deletions
diff --git a/doc/development/testing_guide/testing_migrations_guide.md b/doc/development/testing_guide/testing_migrations_guide.md
new file mode 100644
index 00000000000..03dd7fc7851
--- /dev/null
+++ b/doc/development/testing_guide/testing_migrations_guide.md
@@ -0,0 +1,167 @@
+---
+type: reference
+---
+
+# Testing Rails migrations at GitLab
+
+In order to reliably check Rails migrations, we need to test them against
+a database schema.
+
+## When to write a migration test
+
+- Post migrations (`/db/post_migrate`) and background migrations
+ (`lib/gitlab/background_migration`) **must** have migration tests performed.
+- If your migration is a data migration then it **must** have a migration test.
+- Other migrations may have a migration test if necessary.
+
+## How does it work?
+
+Adding a `:migration` tag to a test signature enables some custom RSpec
+`before` and `after` hooks in our
+[`spec_helper.rb`](https://gitlab.com/gitlab-org/gitlab/blob/3b29908a64ff729c0cf6d93452fe00ab23079c75/spec%2Fspec_helper.rb#L259)
+to run.
+
+A `before` hook will revert all migrations to the point that a migration
+under test is not yet migrated.
+
+In other words, our custom RSpec hooks will find a previous migration, and
+migrate the database **down** to the previous migration version.
+
+With this approach you can test a migration against a database schema.
+
+An `after` hook will migrate the database **up** and reinstitute the latest
+schema version, so that the process does not affect subsequent specs and
+ensures proper isolation.
+
+## Testing an `ActiveRecord::Migration` class
+
+To test an `ActiveRecord::Migration` class (i.e., a
+regular migration `db/migrate` or a post-migration `db/post_migrate`), you
+will need to manually `require` the migration file because it is not
+autoloaded with Rails. Example:
+
+```ruby
+require Rails.root.join('db', 'post_migrate', '20170526185842_migrate_pipeline_stages.rb')
+```
+
+Use the `table` helper to create a temporary `ActiveRecord::Base`-derived model
+for a table. [FactoryBot](https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#factories)
+**should not** be used to create data for migration specs. For example, to
+create a record in the `projects` table:
+
+```ruby
+project = table(:projects).create!(id: 1, name: 'gitlab1', path: 'gitlab1')
+```
+
+Use the `migrate!` helper to run the migration that is under test. It will not only
+run the migration, but will also bump the schema version in the `schema_migrations`
+table. It is necessary because in the `after` hook we trigger the rest of
+the migrations, and we need to know where to start. Example:
+
+```ruby
+it 'migrates successfully' do
+ # ... pre-migration expectations
+
+ migrate!
+
+ # ... post-migration expectations
+end
+```
+
+### Example database migration test
+
+This spec tests the
+[`db/post_migrate/20170526185842_migrate_pipeline_stages.rb`](https://gitlab.com/gitlab-org/gitlab/blob/v11.6.5/db/post_migrate/20170526185842_migrate_pipeline_stages.rb)
+migration. You can find the complete spec in
+[`spec/migrations/migrate_pipeline_stages_spec.rb`](https://gitlab.com/gitlab-org/gitlab/blob/v11.6.5/spec/migrations/migrate_pipeline_stages_spec.rb).
+
+```ruby
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20170526185842_migrate_pipeline_stages.rb')
+
+describe MigratePipelineStages, :migration do
+ # Create test data - pipeline and CI/CD jobs.
+ let(:jobs) { table(:ci_builds) }
+ let(:stages) { table(:ci_stages) }
+ let(:pipelines) { table(:ci_pipelines) }
+ let(:projects) { table(:projects) }
+
+ before do
+ projects.create!(id: 123, name: 'gitlab1', path: 'gitlab1')
+ pipelines.create!(id: 1, project_id: 123, ref: 'master', sha: 'adf43c3a')
+ jobs.create!(id: 1, commit_id: 1, project_id: 123, stage_idx: 2, stage: 'build')
+ jobs.create!(id: 2, commit_id: 1, project_id: 123, stage_idx: 1, stage: 'test')
+ end
+
+ # Test the up migration.
+ it 'correctly migrates pipeline stages' do
+ expect(stages.count).to be_zero
+
+ migrate!
+
+ expect(stages.count).to eq 2
+ expect(stages.all.pluck(:name)).to match_array %w[test build]
+ end
+end
+```
+
+## Testing a non-`ActiveRecord::Migration` class
+
+To test a non-`ActiveRecord::Migration` test (a background migration),
+you will need to manually provide a required schema version. Please add a
+schema tag to a context that you want to switch the database schema within.
+
+Example:
+
+```ruby
+describe SomeClass, :migration, schema: 20170608152748 do
+ # ...
+end
+```
+
+### Example background migration test
+
+This spec tests the
+[`lib/gitlab/background_migration/archive_legacy_traces.rb`](https://gitlab.com/gitlab-org/gitlab/blob/v11.6.5/lib/gitlab/background_migration/archive_legacy_traces.rb)
+background migration. You can find the complete spec on
+[`spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb`](https://gitlab.com/gitlab-org/gitlab/blob/v11.6.5/spec/lib/gitlab/background_migration/archive_legacy_traces_spec.rb)
+
+```ruby
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::ArchiveLegacyTraces, :migration, schema: 20180529152628 do
+ include TraceHelpers
+
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:builds) { table(:ci_builds) }
+ let(:job_artifacts) { table(:ci_job_artifacts) }
+
+ before do
+ namespaces.create!(id: 123, name: 'gitlab1', path: 'gitlab1')
+ projects.create!(id: 123, name: 'gitlab1', path: 'gitlab1', namespace_id: 123)
+ @build = builds.create!(id: 1, project_id: 123, status: 'success', type: 'Ci::Build')
+ end
+
+ context 'when trace file exists at the right place' do
+ before do
+ create_legacy_trace(@build, 'trace in file')
+ end
+
+ it 'correctly archive legacy traces' do
+ expect(job_artifacts.count).to eq(0)
+ expect(File.exist?(legacy_trace_path(@build))).to be_truthy
+
+ described_class.new.perform(1, 1)
+
+ expect(job_artifacts.count).to eq(1)
+ expect(File.exist?(legacy_trace_path(@build))).to be_falsy
+ expect(File.read(archived_trace_path(job_artifacts.first))).to eq('trace in file')
+ end
+ end
+end
+```
+
+NOTE: **Note:**
+These tests do not run within a database transaction, as we use a deletion database
+cleanup strategy. Do not depend on a transaction being present.