From d0151e0a7fd99b9d1d7b5537b3a0020a9db278d1 Mon Sep 17 00:00:00 2001 From: Niklas van Schrick Date: Wed, 26 Feb 2025 20:11:28 +0100 Subject: [PATCH] Extract rubocops from sagittarius --- .rubocop.yml | 4 + lib/rubocop/code0/zero_track/file_helpers.rb | 25 ++++ .../cop/code0/zero_track/logs/rails_logger.rb | 31 ++++ .../migration/create_table_with_timestamps.rb | 72 ++++++++++ .../code0/zero_track/migration/datetime.rb | 53 +++++++ .../code0/zero_track/migration/timestamps.rb | 38 +++++ .../zero_track/migration/versioned_class.rb | 93 ++++++++++++ lib/rubocop/zero_track.rb | 5 + rubocop-zero_track.yml | 17 +++ .../zero_track/logs/rails_logger_spec.rb | 29 ++++ .../create_table_with_timestamps_spec.rb | 132 ++++++++++++++++++ .../zero_track/migration/datetime_spec.rb | 110 +++++++++++++++ .../zero_track/migration/timestamps_spec.rb | 68 +++++++++ .../migration/versioned_class_spec.rb | 99 +++++++++++++ spec/spec_helper.rb | 8 ++ 15 files changed, 784 insertions(+) create mode 100644 lib/rubocop/code0/zero_track/file_helpers.rb create mode 100644 lib/rubocop/cop/code0/zero_track/logs/rails_logger.rb create mode 100644 lib/rubocop/cop/code0/zero_track/migration/create_table_with_timestamps.rb create mode 100644 lib/rubocop/cop/code0/zero_track/migration/datetime.rb create mode 100644 lib/rubocop/cop/code0/zero_track/migration/timestamps.rb create mode 100644 lib/rubocop/cop/code0/zero_track/migration/versioned_class.rb create mode 100644 lib/rubocop/zero_track.rb create mode 100644 rubocop-zero_track.yml create mode 100644 spec/rubocop/cop/code0/zero_track/logs/rails_logger_spec.rb create mode 100644 spec/rubocop/cop/code0/zero_track/migration/create_table_with_timestamps_spec.rb create mode 100644 spec/rubocop/cop/code0/zero_track/migration/datetime_spec.rb create mode 100644 spec/rubocop/cop/code0/zero_track/migration/timestamps_spec.rb create mode 100644 spec/rubocop/cop/code0/zero_track/migration/versioned_class_spec.rb diff --git a/.rubocop.yml b/.rubocop.yml index de9b057..70bb43d 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -62,6 +62,10 @@ Style/ArgumentsForwarding: Style/Documentation: Enabled: false +Style/FormatStringToken: + Exclude: + - spec/rubocop/**/* + Style/HashSyntax: EnforcedShorthandSyntax: never diff --git a/lib/rubocop/code0/zero_track/file_helpers.rb b/lib/rubocop/code0/zero_track/file_helpers.rb new file mode 100644 index 0000000..272dd23 --- /dev/null +++ b/lib/rubocop/code0/zero_track/file_helpers.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module RuboCop + module Code0 + module ZeroTrack + module FileHelpers + def dirname(node) + File.dirname(filepath(node)) + end + + def basename(node) + File.basename(filepath(node)) + end + + def filepath(node) + node.location.expression.source_buffer.name + end + + def in_migration?(node) + dirname(node).end_with?('db/migrate') + end + end + end + end +end diff --git a/lib/rubocop/cop/code0/zero_track/logs/rails_logger.rb b/lib/rubocop/cop/code0/zero_track/logs/rails_logger.rb new file mode 100644 index 0000000..7818055 --- /dev/null +++ b/lib/rubocop/cop/code0/zero_track/logs/rails_logger.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Code0 + module ZeroTrack + module Logs + # Cop that checks if 'timestamps' method is called with timezone information. + class RailsLogger < RuboCop::Cop::Base + MSG = 'Do not use `Rails.logger` directly, include `Code0::ZeroTrack::Loggable` instead' + LOG_METHODS = %i[debug error fatal info warn].freeze + LOG_METHODS_PATTERN = LOG_METHODS.map(&:inspect).join(' ').freeze + + def_node_matcher :rails_logger_log?, <<~PATTERN + (send + (send (const nil? :Rails) :logger) + {#{LOG_METHODS_PATTERN}} ... + ) + PATTERN + + def on_send(node) + return unless rails_logger_log?(node) + + add_offense(node) + end + end + end + end + end + end +end diff --git a/lib/rubocop/cop/code0/zero_track/migration/create_table_with_timestamps.rb b/lib/rubocop/cop/code0/zero_track/migration/create_table_with_timestamps.rb new file mode 100644 index 0000000..8c91545 --- /dev/null +++ b/lib/rubocop/cop/code0/zero_track/migration/create_table_with_timestamps.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require_relative '../../../../code0/zero_track/file_helpers' + +module RuboCop + module Cop + module Code0 + module ZeroTrack + module Migration + class CreateTableWithTimestamps < RuboCop::Cop::Base + include RuboCop::Code0::ZeroTrack::FileHelpers + + MSG = 'Add timestamps when creating a new table.' + RESTRICT_ON_SEND = %i[create_table].freeze + + def_node_matcher :create_table_with_timestamps_proc?, <<~PATTERN + (send nil? :create_table (sym _) ... (block-pass (sym :timestamps_with_timezone))) + PATTERN + + def_node_search :timestamps_included?, <<~PATTERN + (send _var :timestamps_with_timezone ...) + PATTERN + + def_node_search :created_at_included?, <<~PATTERN + (send _var :datetime_with_timezone + {(sym :created_at)(str "created_at")} + ...) + PATTERN + + def_node_search :updated_at_included?, <<~PATTERN + (send _var :datetime_with_timezone + {(sym :updated_at)(str "updated_at")} + ...) + PATTERN + + def_node_matcher :create_table_with_block?, <<~PATTERN + (block + (send nil? :create_table ...) + (args (arg _var)+) + _) + PATTERN + + def on_send(node) + return unless in_migration?(node) + return unless node.command?(:create_table) + + parent = node.parent + + if create_table_with_block?(parent) + add_offense(parent) if parent.body.nil? || !time_columns_included?(parent.body) + elsif create_table_with_timestamps_proc?(node) + # nothing to do + else + add_offense(node) + end + end + + private + + def time_columns_included?(node) + timestamps_included?(node) || created_at_and_updated_at_included?(node) + end + + def created_at_and_updated_at_included?(node) + created_at_included?(node) && updated_at_included?(node) + end + end + end + end + end + end +end diff --git a/lib/rubocop/cop/code0/zero_track/migration/datetime.rb b/lib/rubocop/cop/code0/zero_track/migration/datetime.rb new file mode 100644 index 0000000..b90fd13 --- /dev/null +++ b/lib/rubocop/cop/code0/zero_track/migration/datetime.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require_relative '../../../../code0/zero_track/file_helpers' + +module RuboCop + module Cop + module Code0 + module ZeroTrack + module Migration + # Cop that checks if datetime data type is added with timezone information. + class Datetime < RuboCop::Cop::Base + include RuboCop::Code0::ZeroTrack::FileHelpers + extend AutoCorrector + + MSG = 'Do not use the `%s` data type, use `datetime_with_timezone` instead' + + # Check methods in table creation. + def on_def(node) + return unless in_migration?(node) + + node.each_descendant(:send) do |send_node| + method_name = send_node.children[1] + + next unless %i[datetime timestamp].include?(method_name) + + add_offense(send_node.loc.selector, message: format(MSG, method_name)) do |corrector| + corrector.replace(send_node.loc.selector, 'datetime_with_timezone') + end + end + end + + # Check methods. + def on_send(node) + return unless in_migration?(node) + + node.each_descendant do |descendant| + next unless descendant.type == :sym + + last_argument = descendant.children.last + + next unless %i[datetime timestamp].include?(last_argument) + + add_offense(descendant, message: format(MSG, last_argument)) do |corrector| + corrector.replace(descendant, ':datetime_with_timezone') + end + end + end + end + end + end + end + end +end diff --git a/lib/rubocop/cop/code0/zero_track/migration/timestamps.rb b/lib/rubocop/cop/code0/zero_track/migration/timestamps.rb new file mode 100644 index 0000000..303033f --- /dev/null +++ b/lib/rubocop/cop/code0/zero_track/migration/timestamps.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require_relative '../../../../code0/zero_track/file_helpers' + +module RuboCop + module Cop + module Code0 + module ZeroTrack + module Migration + # Cop that checks if 'timestamps' method is called with timezone information. + class Timestamps < RuboCop::Cop::Base + include RuboCop::Code0::ZeroTrack::FileHelpers + extend AutoCorrector + + MSG = 'Do not use `timestamps`, use `timestamps_with_timezone` instead' + + # Check methods in table creation. + def on_def(node) + return unless in_migration?(node) + + node.each_descendant(:send) do |send_node| + next unless method_name(send_node) == :timestamps + + add_offense(send_node.loc.selector) do |corrector| + corrector.replace(send_node.loc.selector, 'timestamps_with_timezone') + end + end + end + + def method_name(node) + node.children[1] + end + end + end + end + end + end +end diff --git a/lib/rubocop/cop/code0/zero_track/migration/versioned_class.rb b/lib/rubocop/cop/code0/zero_track/migration/versioned_class.rb new file mode 100644 index 0000000..f3b99fb --- /dev/null +++ b/lib/rubocop/cop/code0/zero_track/migration/versioned_class.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +require_relative '../../../../code0/zero_track/file_helpers' + +module RuboCop + module Cop + module Code0 + module ZeroTrack + module Migration + class VersionedClass < RuboCop::Cop::Base + include RuboCop::Code0::ZeroTrack::FileHelpers + extend AutoCorrector + + MIGRATION_CLASS = 'Code0::ZeroTrack::Database::Migration' + + # rubocop:disable Layout/LineLength + MSG_WRONG_BASE_CLASS = "Don't use `%s`. Use `#{MIGRATION_CLASS}` instead.".freeze + MSG_WRONG_VERSION = "Don't use version `%s` of `#{MIGRATION_CLASS}`. Use version `%s` instead.".freeze + # rubocop:enable Layout/LineLength + + def on_class(node) + return unless in_migration?(node) + + return on_zerotrack_migration(node) if zerotrack_migration?(node) + + add_offense( + node.parent_class, + message: format(MSG_WRONG_BASE_CLASS, base_class: superclass(node)) + ) do |corrector| + corrector.replace(node.parent_class, "#{MIGRATION_CLASS}[#{find_allowed_versions(node).last}]") + end + end + + private + + def on_zerotrack_migration(node) + return if cop_config['AllowedVersions'].nil? # allow all versions if nothing configured + return if correct_migration_version?(node) + + current_version = get_migration_version(node) + allowed_version = find_allowed_versions(node).last + + version_node = get_migration_version_node(node) + + add_offense( + version_node, + message: format(MSG_WRONG_VERSION, current_version: current_version, allowed_version: allowed_version) + ) do |corrector| + corrector.replace(version_node, find_allowed_versions(node).last.to_s) + end + end + + def zerotrack_migration?(node) + superclass(node) == MIGRATION_CLASS + end + + def superclass(class_node) + _, *others = class_node.descendants + + others.find { |node| node.const_type? && node.const_name != 'Types' }&.const_name + end + + def correct_migration_version?(node) + find_allowed_versions(node).include?(get_migration_version(node)) + end + + def get_migration_version_node(node) + node.parent_class.arguments[0] + end + + def get_migration_version(node) + get_migration_version_node(node).value + end + + def find_allowed_versions(node) + migration_version = basename(node).split('_').first.to_i + allowed_versions.select do |range, _| + range.include?(migration_version) + end.values + end + + def allowed_versions + cop_config['AllowedVersions'].transform_keys do |range| + range_ints = range.split('..').map(&:to_i) + range_ints[0]..range_ints[1] + end + end + end + end + end + end + end +end diff --git a/lib/rubocop/zero_track.rb b/lib/rubocop/zero_track.rb new file mode 100644 index 0000000..8438858 --- /dev/null +++ b/lib/rubocop/zero_track.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +# rubocop:disable Lint/RedundantDirGlobSort +Dir[File.join(__dir__, 'cop', '**', '*.rb')].sort.each { |file| require file } +# rubocop:enable Lint/RedundantDirGlobSort diff --git a/rubocop-zero_track.yml b/rubocop-zero_track.yml new file mode 100644 index 0000000..e27379c --- /dev/null +++ b/rubocop-zero_track.yml @@ -0,0 +1,17 @@ +require: + - ./lib/rubocop/zero_track + +Code0/ZeroTrack/Logs/RailsLogger: + Enabled: false + +Code0/ZeroTrack/Migration/CreateTableWithTimestamps: + Enabled: false + +Code0/ZeroTrack/Migration/Datetime: + Enabled: false + +Code0/ZeroTrack/Migration/Timestamps: + Enabled: false + +Code0/ZeroTrack/Migration/VersionedClass: + Enabled: false diff --git a/spec/rubocop/cop/code0/zero_track/logs/rails_logger_spec.rb b/spec/rubocop/cop/code0/zero_track/logs/rails_logger_spec.rb new file mode 100644 index 0000000..3a296c6 --- /dev/null +++ b/spec/rubocop/cop/code0/zero_track/logs/rails_logger_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe RuboCop::Cop::Code0::ZeroTrack::Logs::RailsLogger, type: :rubocop do + described_class::LOG_METHODS.each do |method| + it "flags the use of Rails.logger.#{method} with a constant receiver" do + node = "Rails.logger.#{method}('some error')" + msg = 'Do not use `Rails.logger` directly, include `Code0::ZeroTrack::Loggable` instead' + + expect_offense(<<~CODE, node: node, msg: msg) + %{node} + ^{node} %{msg} + CODE + end + end + + it 'does not flag the use of Rails.logger with a constant that is not Rails' do + expect_no_offenses("AppLogger.error('some error')") + end + + it 'does not flag the use of logger with a send receiver' do + expect_no_offenses("file_logger.info('important info')") + end + + it 'does not flag the use of Rails.logger.level' do + expect_no_offenses('Rails.logger.level') + end +end diff --git a/spec/rubocop/cop/code0/zero_track/migration/create_table_with_timestamps_spec.rb b/spec/rubocop/cop/code0/zero_track/migration/create_table_with_timestamps_spec.rb new file mode 100644 index 0000000..011d6a2 --- /dev/null +++ b/spec/rubocop/cop/code0/zero_track/migration/create_table_with_timestamps_spec.rb @@ -0,0 +1,132 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe RuboCop::Cop::Code0::ZeroTrack::Migration::CreateTableWithTimestamps, type: :rubocop do + context 'when inside of migration' do + before do + allow(cop).to receive(:in_migration?).and_return(true) + end + + it 'registers an offense when the a table without timestamps is created' do + expect_offense(<<~CODE) + class CreateUsers < ActiveRecord::Migration[7.0] + def change + create_table :users do |t| + ^^^^^^^^^^^^^^^^^^^^^^^^^^ Add timestamps when creating a new table. + t.string :username + end + end + end + CODE + end + + context "when created_at and updated_at are not 'datetime_with_timezone'" do + it "registers an offense when 'created_at' has the wrong type" do + expect_offense(<<~CODE) + class Users < ActiveRecord::Migration[7.0] + def change + create_table :users do |t| + ^^^^^^^^^^^^^^^^^^^^^^^^^^ Add timestamps when creating a new table. + t.string :username, null: false + t.string :created_at, null: true + t.datetime_with_timezone :updated_at, null: true + t.string :password + end + end + end + CODE + end + + it "registers an offense when 'updated_at' has the wrong type" do + expect_offense(<<~CODE) + class Users < ActiveRecord::Migration[7.0] + def change + create_table :users do |t| + ^^^^^^^^^^^^^^^^^^^^^^^^^^ Add timestamps when creating a new table. + t.string :username, null: false + t.datetime_with_timezone :created_at, null: true + t.string :updated_at, null: true + t.string :password + end + end + end + CODE + end + + it "registers an offense when 'created_at' and 'updated_at' have the wrong type" do + expect_offense(<<~CODE) + class Users < ActiveRecord::Migration[7.0] + def change + create_table :users do |t| + ^^^^^^^^^^^^^^^^^^^^^^^^^^ Add timestamps when creating a new table. + t.string :username, null: false + t.string :created_at, null: true + t.string :updated_at, null: true + t.string :password + end + end + end + CODE + end + end + + it "registers no offense when both 'created_at' and 'updated_at' are defined manually" do + expect_no_offenses(<<~CODE) + class Users < ActiveRecord::Migration[7.0] + def change + create_table :users do |t| + t.string :username, null: false + t.datetime_with_timezone :created_at, null: true + t.datetime_with_timezone :updated_at, null: true + t.string :password + end + end + end + CODE + end + + context 'when multi-arg block from migration helper is used' do + it 'registers no offense when timestamps are present' do + expect_no_offenses(<<~CODE) + class User < Sagittarius::Database::Migration[1.0] + def change + create_table :users do |t, helper| + t.string :username, null: false + + t.timestamps_with_timezone + end + end + end + CODE + end + + it 'registers an offense when timestamps are not present' do + expect_offense(<<~CODE) + class CreateUsers < ActiveRecord::Migration[7.0] + def change + create_table :users do |t, helper| + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Add timestamps when creating a new table. + t.string :username + end + end + end + CODE + end + end + end + + context 'when outside of migration' do + it 'registers no offense' do + expect_no_offenses(<<~CODE) + class CreateUsers < ActiveRecord::Migration[7.0] + def change + create_table :users do |t| + t.string :username + end + end + end + CODE + end + end +end diff --git a/spec/rubocop/cop/code0/zero_track/migration/datetime_spec.rb b/spec/rubocop/cop/code0/zero_track/migration/datetime_spec.rb new file mode 100644 index 0000000..aabb90d --- /dev/null +++ b/spec/rubocop/cop/code0/zero_track/migration/datetime_spec.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe RuboCop::Cop::Code0::ZeroTrack::Migration::Datetime, type: :rubocop do + context 'when inside of migration' do + before do + allow(cop).to receive(:in_migration?).and_return(true) + end + + %w[datetime timestamp].each do |method| + it "registers an offense when the \"#{method}\" method is used" do + expect_offense(<<~CODE) + class Users < ActiveRecord::Migration[4.2] + def change + create_table :users do |t| + t.string :username, null: false + t.#{method} :created_at, null: true + #{'^' * method.length} Do not use the `#{method}` data type, use `datetime_with_timezone` instead + t.string :password + end + end + end + CODE + + expect_correction(<<~CODE) + class Users < ActiveRecord::Migration[4.2] + def change + create_table :users do |t| + t.string :username, null: false + t.datetime_with_timezone :created_at, null: true + t.string :password + end + end + end + CODE + end + + it "registers an offense when the \"#{method}\" argument is used" do + expect_offense(<<~CODE) + class Users < ActiveRecord::Migration[4.2] + def change + add_column :users, :confirmed_at, :#{method} + ^#{'^' * method.length} Do not use the `#{method}` data type, use `datetime_with_timezone` instead + end + end + CODE + + expect_correction(<<~CODE) + class Users < ActiveRecord::Migration[4.2] + def change + add_column :users, :confirmed_at, :datetime_with_timezone + end + end + CODE + end + + it "registers an offense when the \"#{method}\" argument is used and keyword arguments are present" do + expect_offense(<<~CODE) + class Users < ActiveRecord::Migration[4.2] + def change + add_column :users, :confirmed_at, :#{method}, null: false + ^#{'^' * method.length} Do not use the `#{method}` data type, use `datetime_with_timezone` instead + end + end + CODE + + expect_correction(<<~CODE) + class Users < ActiveRecord::Migration[4.2] + def change + add_column :users, :confirmed_at, :datetime_with_timezone, null: false + end + end + CODE + end + end + + it 'registers no offense when datetime_with_timezone is used' do + expect_no_offenses(<<~CODE) + class Users < ActiveRecord::Migration[4.2] + def change + create_table :users do |t| + t.string :username, null: false + t.datetime_with_timezone :created_at, null: true + t.string :password + end + end + end + CODE + end + end + + context 'when outside of migration' do + %w[datetime timestamp].each do |method| + it "registers no offense when using \"#{method}\"" do + expect_no_offenses(<<~CODE) + class Users < ActiveRecord::Migration[4.2] + def change + create_table :users do |t| + t.string :username, null: false + t.#{method} :created_at, null: true + t.string :password + end + end + end + CODE + end + end + end +end diff --git a/spec/rubocop/cop/code0/zero_track/migration/timestamps_spec.rb b/spec/rubocop/cop/code0/zero_track/migration/timestamps_spec.rb new file mode 100644 index 0000000..6c753d3 --- /dev/null +++ b/spec/rubocop/cop/code0/zero_track/migration/timestamps_spec.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe RuboCop::Cop::Code0::ZeroTrack::Migration::Timestamps, type: :rubocop do + context 'when inside of migration' do + before do + allow(cop).to receive(:in_migration?).and_return(true) + end + + it 'registers an offense when the "timestamps" method is used' do + expect_offense(<<~CODE) + class Users < ActiveRecord::Migration[4.2] + def change + create_table :users do |t| + t.string :username, null: false + t.timestamps null: true + ^^^^^^^^^^ Do not use `timestamps`, use `timestamps_with_timezone` instead + t.string :password + end + end + end + CODE + + expect_correction(<<~CODE) + class Users < ActiveRecord::Migration[4.2] + def change + create_table :users do |t| + t.string :username, null: false + t.timestamps_with_timezone null: true + t.string :password + end + end + end + CODE + end + + it 'registers no offense when timestamps_with_timezone is used' do + expect_no_offenses(<<~CODE) + class Users < ActiveRecord::Migration[4.2] + def change + create_table :users do |t| + t.string :username, null: false + t.timestamps_with_timezone null: true + t.string :password + end + end + end + CODE + end + end + + context 'when outside of migration' do + it 'registers no offense' do + expect_no_offenses(<<~CODE) + class Users < ActiveRecord::Migration[4.2] + def change + create_table :users do |t| + t.string :username, null: false + t.timestamps null: true + t.string :password + end + end + end + CODE + end + end +end diff --git a/spec/rubocop/cop/code0/zero_track/migration/versioned_class_spec.rb b/spec/rubocop/cop/code0/zero_track/migration/versioned_class_spec.rb new file mode 100644 index 0000000..de14f70 --- /dev/null +++ b/spec/rubocop/cop/code0/zero_track/migration/versioned_class_spec.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe RuboCop::Cop::Code0::ZeroTrack::Migration::VersionedClass, type: :rubocop do + context 'when inside of migration' do + before do + allow(cop).to receive_messages( + in_migration?: true, + basename: '20231129173717_create_users', + cop_config: { 'AllowedVersions' => { '2023_11_29_17_37_16..' => 1.0 } } + ) + end + + it 'registers an offense when the "ActiveRecord::Migration" class is used' do + expect_offense(<<~CODE) + class Users < ActiveRecord::Migration[4.2] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't use `ActiveRecord::Migration`. Use `Code0::ZeroTrack::Database::Migration` instead. + def change + create_table :users do |t| + t.string :username, null: false + t.timestamps_with_timezone null: true + t.string :password + end + end + end + CODE + + expect_correction(<<~CODE) + class Users < Code0::ZeroTrack::Database::Migration[1.0] + def change + create_table :users do |t| + t.string :username, null: false + t.timestamps_with_timezone null: true + t.string :password + end + end + end + CODE + end + + it 'registers an offense when the wrong version of "Code0::ZeroTrack::Database::Migration" is used' do + expect_offense(<<~CODE) + class Users < Code0::ZeroTrack::Database::Migration[1.1] + ^^^ Don't use version `1.1` of `Code0::ZeroTrack::Database::Migration`. Use version `1.0` instead. + def change + create_table :users do |t| + t.string :username, null: false + t.timestamps_with_timezone null: true + t.string :password + end + end + end + CODE + + expect_correction(<<~CODE) + class Users < Code0::ZeroTrack::Database::Migration[1.0] + def change + create_table :users do |t| + t.string :username, null: false + t.timestamps_with_timezone null: true + t.string :password + end + end + end + CODE + end + + it 'registers no offense when correct version is used' do + expect_no_offenses(<<~CODE) + class Users < Code0::ZeroTrack::Database::Migration[1.0] + def change + create_table :users do |t| + t.string :username, null: false + t.timestamps_with_timezone null: true + t.string :password + end + end + end + CODE + end + end + + context 'when outside of migration' do + it 'registers no offense' do + expect_no_offenses(<<~CODE) + class Users < ActiveRecord::Migration[4.2] + def change + create_table :users do |t| + t.string :username, null: false + t.timestamps_with_timezone null: true + t.string :password + end + end + end + CODE + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 5daba99..6fc1f92 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -14,6 +14,11 @@ require 'rspec-parameterized' +require 'rubocop' +require 'rubocop/rspec/support' + +require 'rubocop/zero_track' # our entrypoint + RSpec.configure do |config| # Enable flags like --only-failures and --next-failure config.example_status_persistence_file_path = '.rspec_status' @@ -28,4 +33,7 @@ config.define_derived_metadata do |metadata| metadata[:aggregate_failures] = true end + + config.include_context 'config', type: :rubocop + config.include RuboCop::RSpec::ExpectOffense, type: :rubocop end