summaryrefslogtreecommitdiff
path: root/spec/migrations/cleanup_projects_with_missing_namespace_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/migrations/cleanup_projects_with_missing_namespace_spec.rb')
-rw-r--r--spec/migrations/cleanup_projects_with_missing_namespace_spec.rb134
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