diff options
Diffstat (limited to 'spec/migrations/cleanup_projects_with_missing_namespace_spec.rb')
-rw-r--r-- | spec/migrations/cleanup_projects_with_missing_namespace_spec.rb | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/spec/migrations/cleanup_projects_with_missing_namespace_spec.rb b/spec/migrations/cleanup_projects_with_missing_namespace_spec.rb new file mode 100644 index 00000000000..06b6d5e3b46 --- /dev/null +++ b/spec/migrations/cleanup_projects_with_missing_namespace_spec.rb @@ -0,0 +1,134 @@ +# frozen_string_literal: true + +require 'spec_helper' + +require Rails.root.join('db', 'post_migrate', '20200511080113_add_projects_foreign_key_to_namespaces.rb') +require Rails.root.join('db', 'post_migrate', '20200511083541_cleanup_projects_with_missing_namespace.rb') + +LOST_AND_FOUND_GROUP = 'lost-and-found' +USER_TYPE_GHOST = 5 +ACCESS_LEVEL_OWNER = 50 + +# In order to test the CleanupProjectsWithMissingNamespace migration, we need +# to first create an orphaned project (one with an invalid namespace_id) +# and then run the migration to check that the project was properly cleaned up +# +# The problem is that the CleanupProjectsWithMissingNamespace migration comes +# after the FK has been added with a previous migration (AddProjectsForeignKeyToNamespaces) +# That means that while testing the current class we can not insert projects with an +# invalid namespace_id as the existing FK is correctly blocking us from doing so +# +# The approach that solves that problem is to: +# - Set the schema of this test to the one prior to AddProjectsForeignKeyToNamespaces +# - We could hardcode it to `20200508091106` (which currently is the previous +# migration before adding the FK) but that would mean that this test depends +# on migration 20200508091106 not being reverted or deleted +# - So, we use SchemaVersionFinder that finds the previous migration and returns +# its schema, which we then use in the describe +# +# That means that we lock the schema version to the one returned by +# SchemaVersionFinder.previous_migration and only test the cleanup migration +# *without* the migration that adds the Foreign Key ever running +# That's acceptable as the cleanup script should not be affected in any way +# by the migration that adds the Foreign Key +class SchemaVersionFinder + def self.migrations_paths + ActiveRecord::Migrator.migrations_paths + end + + def self.migration_context + ActiveRecord::MigrationContext.new(migrations_paths, ActiveRecord::SchemaMigration) + end + + def self.migrations + migration_context.migrations + end + + def self.previous_migration + migrations.each_cons(2) do |previous, migration| + break previous.version if migration.name == AddProjectsForeignKeyToNamespaces.name + end + end +end + +describe CleanupProjectsWithMissingNamespace, :migration, schema: SchemaVersionFinder.previous_migration do + let(:projects) { table(:projects) } + let(:namespaces) { table(:namespaces) } + let(:users) { table(:users) } + + before do + namespace = namespaces.create!(name: 'existing_namespace', path: 'existing_namespace') + + projects.create!( + name: 'project_with_existing_namespace', + path: 'project_with_existing_namespace', + visibility_level: 20, + archived: false, + namespace_id: namespace.id + ) + + projects.create!( + name: 'project_with_non_existing_namespace', + path: 'project_with_non_existing_namespace', + visibility_level: 20, + archived: false, + namespace_id: non_existing_record_id + ) + end + + it 'creates the ghost user' do + expect(users.where(user_type: USER_TYPE_GHOST).count).to eq(0) + + disable_migrations_output { migrate! } + + expect(users.where(user_type: USER_TYPE_GHOST).count).to eq(1) + end + + it 'creates the lost-and-found group, owned by the ghost user' do + expect( + Group.where(Group.arel_table[:name].matches("#{LOST_AND_FOUND_GROUP}%")).count + ).to eq(0) + + disable_migrations_output { migrate! } + + ghost_user = users.find_by(user_type: USER_TYPE_GHOST) + expect( + Group + .joins('INNER JOIN members ON namespaces.id = members.source_id') + .where('namespaces.type = ?', 'Group') + .where('members.type = ?', 'GroupMember') + .where('members.source_type = ?', 'Namespace') + .where('members.user_id = ?', ghost_user.id) + .where('members.requested_at IS NULL') + .where('members.access_level = ?', ACCESS_LEVEL_OWNER) + .where(Group.arel_table[:name].matches("#{LOST_AND_FOUND_GROUP}%")) + .count + ).to eq(1) + end + + it 'moves the orphaned project to the lost-and-found group' do + orphaned_project = projects.find_by(name: 'project_with_non_existing_namespace') + expect(orphaned_project.visibility_level).to eq(20) + expect(orphaned_project.archived).to eq(false) + expect(orphaned_project.namespace_id).to eq(non_existing_record_id) + + disable_migrations_output { migrate! } + + lost_and_found_group = Group.find_by(Group.arel_table[:name].matches("#{LOST_AND_FOUND_GROUP}%")) + orphaned_project = projects.find_by(id: orphaned_project.id) + + expect(orphaned_project.visibility_level).to eq(0) + expect(orphaned_project.namespace_id).to eq(lost_and_found_group.id) + expect(orphaned_project.name).to eq("project_with_non_existing_namespace_#{orphaned_project.id}") + expect(orphaned_project.path).to eq("project_with_non_existing_namespace_#{orphaned_project.id}") + expect(orphaned_project.archived).to eq(true) + + valid_project = projects.find_by(name: 'project_with_existing_namespace') + existing_namespace = namespaces.find_by(name: 'existing_namespace') + + expect(valid_project.visibility_level).to eq(20) + expect(valid_project.namespace_id).to eq(existing_namespace.id) + expect(valid_project.path).to eq('project_with_existing_namespace') + expect(valid_project.archived).to eq(false) + end +end |