# frozen_string_literal: true require 'fast_spec_helper' require_relative '../../../../rubocop/cop/migration/create_table_with_foreign_keys' RSpec.describe RuboCop::Cop::Migration::CreateTableWithForeignKeys do let(:cop) { described_class.new } context 'outside of a migration' do it 'does not register any offenses' do expect_no_offenses(<<~RUBY) def up create_table(:foo) do |t| t.references :bar, foreign_key: { on_delete: 'cascade' } t.references :zoo, foreign_key: { on_delete: 'cascade' } end end RUBY end end context 'when in a migration' do before do allow(cop).to receive(:in_migration?).and_return(true) end context 'without foreign key' do it 'does not register any offenses' do expect_no_offenses(<<~RUBY) def up create_table(:foo) do |t| t.references :bar end end RUBY end end context 'with foreign key' do context 'with just one foreign key' do context 'when the foreign_key targets a high traffic table' do context 'when the foreign_key has to_table option set' do it 'does not register any offenses' do expect_no_offenses(<<~RUBY) def up create_table(:foo) do |t| t.references :project, "foreign_key" => { on_delete: 'cascade', to_table: 'projects' } end end RUBY end end context 'when the foreign_key does not have to_table option set' do it 'does not register any offenses' do expect_no_offenses(<<~RUBY) def up create_table(:foo) do |t| t.references :project, foreign_key: { on_delete: 'cascade' } end end RUBY end end end context 'when the foreign_key does not target a high traffic table' do it 'does not register any offenses' do expect_no_offenses(<<~RUBY) def up create_table(:foo) do |t| t.references :bar, foreign_key: { on_delete: 'cascade' } end end RUBY end end end context 'with more than one foreign keys' do let(:offense) do 'Creating a table with more than one foreign key at once violates our migration style guide. ' \ 'For more details check the https://docs.gitlab.com/ee/development/migration_style_guide.html#examples' end shared_examples 'target to high traffic table' do |dsl_method, table_name| context 'when the target is defined as option' do it 'registers an offense' do expect_offense(<<~RUBY) def up create_table(:foo) do |t| ^^^^^^^^^^^^^^^^^^ #{offense} t.#{dsl_method} :#{table_name.singularize} #{explicit_target_opts} t.#{dsl_method} :zoo #{implicit_target_opts} end end RUBY end end context 'when the target has implicit definition' do it 'registers an offense' do expect_offense(<<~RUBY) def up create_table(:foo) do |t| ^^^^^^^^^^^^^^^^^^ #{offense} t.#{dsl_method} :#{table_name.singularize} #{implicit_target_opts} t.#{dsl_method} :zoo #{implicit_target_opts} end end RUBY end end end shared_context 'when there is a target to a high traffic table' do |dsl_method| %w[ audit_events ci_builds ci_builds_metadata ci_job_artifacts ci_pipeline_variables ci_pipelines ci_stages deployments events gitlab_subscriptions issues merge_request_diff_commits merge_request_diff_files merge_request_diffs merge_request_metrics merge_requests namespaces note_diff_files notes project_authorizations projects project_ci_cd_settings project_features push_event_payloads resource_label_events routes sent_notifications system_note_metadata taggings todos users web_hook_logs ].each do |table| let(:table_name) { table } it_behaves_like 'target to high traffic table', dsl_method, table end end context 'when the foreign keys are defined as options' do context 'when there is no target to a high traffic table' do it 'does not register any offenses' do expect_no_offenses(<<~RUBY) def up create_table(:foo) do |t| t.references :bar, foreign_key: { on_delete: 'cascade', to_table: :bars } t.references :zoo, 'foreign_key' => { on_delete: 'cascade' } t.references :john, 'foreign_key' => { on_delete: 'cascade' } t.references :doe, 'foreign_key' => { on_delete: 'cascade', to_table: 'doe' } end end RUBY end end include_context 'when there is a target to a high traffic table', :references do let(:explicit_target_opts) { ", foreign_key: { to_table: :#{table_name} }" } let(:implicit_target_opts) { ", foreign_key: true" } end end context 'when the foreign keys are defined by standlone migration helper' do context 'when there is no target to a high traffic table' do it 'does not register any offenses' do expect_no_offenses(<<~RUBY) def up create_table(:foo) do |t| t.foreign_key :bar t.foreign_key :zoo, to_table: 'zoos' end end RUBY end end include_context 'when there is a target to a high traffic table', :foreign_key do let(:explicit_target_opts) { ", to_table: :#{table_name}" } let(:implicit_target_opts) { } end end end end end end