diff --git a/lib/database_consistency.rb b/lib/database_consistency.rb index 4a82d8d..ec68a22 100644 --- a/lib/database_consistency.rb +++ b/lib/database_consistency.rb @@ -39,6 +39,7 @@ require 'database_consistency/writers/simple/enum_values_inconsistent_with_inclusion' require 'database_consistency/writers/simple/redundant_case_insensitive_option' require 'database_consistency/writers/simple/three_state_boolean' +require 'database_consistency/writers/simple/enum_column' require 'database_consistency/writers/simple_writer' require 'database_consistency/writers/autofix/helpers/migration' @@ -60,6 +61,7 @@ require 'database_consistency/checkers/enum_checkers/enum_checker' require 'database_consistency/checkers/enum_checkers/enum_type_checker' +require 'database_consistency/checkers/enum_checkers/enum_column_checker' require 'database_consistency/checkers/association_checkers/association_checker' require 'database_consistency/checkers/association_checkers/missing_index_checker' diff --git a/lib/database_consistency/checkers/enum_checkers/enum_column_checker.rb b/lib/database_consistency/checkers/enum_checkers/enum_column_checker.rb new file mode 100644 index 0000000..b231960 --- /dev/null +++ b/lib/database_consistency/checkers/enum_checkers/enum_column_checker.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module DatabaseConsistency + module Checkers + # This class checks that ActiveRecord enum is backed by enum column + class EnumColumnChecker < EnumChecker + Report = ReportBuilder.define( + DatabaseConsistency::Report, + :table_name, + :column_name + ) + + private + + # ActiveRecord supports native enum type since version 7 and only for PostgreSQL + def preconditions + Helper.postgresql? && ActiveRecord::VERSION::MAJOR >= 7 && column.present? + end + + def check + if valid? + report_template(:ok) + else + report_template(:fail, error_slug: :enum_column_type_mismatch) + end + end + + def report_template(status, error_slug: nil) + Report.new( + status: status, + error_slug: error_slug, + error_message: nil, + table_name: model.table_name, + column_name: column.name, + **report_attributes + ) + end + + def column + @column ||= model.columns.find { |c| c.name.to_s == enum.to_s } + end + + # @return [Boolean] + def valid? + column.type == :enum + end + end + end +end diff --git a/lib/database_consistency/processors/enums_processor.rb b/lib/database_consistency/processors/enums_processor.rb index 97d9e68..09f3c19 100644 --- a/lib/database_consistency/processors/enums_processor.rb +++ b/lib/database_consistency/processors/enums_processor.rb @@ -5,7 +5,8 @@ module Processors # The class to process enums class EnumsProcessor < BaseProcessor CHECKERS = [ - Checkers::EnumTypeChecker + Checkers::EnumTypeChecker, + Checkers::EnumColumnChecker ].freeze private diff --git a/lib/database_consistency/writers/simple/enum_column.rb b/lib/database_consistency/writers/simple/enum_column.rb new file mode 100644 index 0000000..04a73b7 --- /dev/null +++ b/lib/database_consistency/writers/simple/enum_column.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module DatabaseConsistency + module Writers + module Simple + class EnumColumn < Base # :nodoc: + private + + def template + 'column should be enum type' + end + + def unique_attributes + { + table_name: report.table_name, + column_name: report.column_name + } + end + end + end + end +end diff --git a/lib/database_consistency/writers/simple_writer.rb b/lib/database_consistency/writers/simple_writer.rb index 96dd783..dac36d7 100644 --- a/lib/database_consistency/writers/simple_writer.rb +++ b/lib/database_consistency/writers/simple_writer.rb @@ -29,7 +29,8 @@ class SimpleWriter < BaseWriter enum_values_inconsistent_with_ar_enum: Simple::EnumValuesInconsistentWithArEnum, enum_values_inconsistent_with_inclusion: Simple::EnumValuesInconsistentWithInclusion, redundant_case_insensitive_option: Simple::RedundantCaseInsensitiveOption, - three_state_boolean: Simple::ThreeStateBoolean + three_state_boolean: Simple::ThreeStateBoolean, + enum_column_type_mismatch: Simple::EnumColumn }.freeze def write diff --git a/spec/checkers/enum_column_checker/postgresql_spec.rb b/spec/checkers/enum_column_checker/postgresql_spec.rb new file mode 100644 index 0000000..c416548 --- /dev/null +++ b/spec/checkers/enum_column_checker/postgresql_spec.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +RSpec.describe DatabaseConsistency::Checkers::EnumColumnChecker, :postgresql do + subject(:checker) { described_class.new(model, enum) } + + before do + skip('older versions are not supported') if ActiveRecord::VERSION::MAJOR < 7 + end + + let(:model) { entity_class } + let(:enum) { entity_class.defined_enums.keys.first } + + context 'with enum column' do + let(:entity_class) do + define_class do |klass| + klass.enum field: { value1: 'value1', value2: 'value2' } + end + end + + before do + define_database do + create_enum :field_type, %w[value1 value2] + + create_table :entities do |t| + t.enum :field, enum_type: 'field_type' + end + end + end + + specify do + expect(checker.report).to have_attributes( + checker_name: 'EnumColumnChecker', + table_or_model_name: entity_class.name, + column_or_attribute_name: 'field', + status: :ok, + error_message: nil, + error_slug: nil + ) + end + end + + context 'with integer column' do + let(:entity_class) do + define_class do |klass| + klass.enum field: %i[value1 value2] + end + end + + before do + define_database do + create_table :entities do |t| + t.integer :field + end + end + end + + specify do + expect(checker.report).to have_attributes( + checker_name: 'EnumColumnChecker', + table_or_model_name: entity_class.name, + column_or_attribute_name: 'field', + status: :fail, + error_message: nil, + error_slug: :enum_column_type_mismatch + ) + end + end + + context 'with string column' do + let(:entity_class) do + define_class do |klass| + klass.enum field: { value1: 'value1', value2: 'value2' } + end + end + + before do + define_database do + create_table :entities do |t| + t.string :field + end + end + end + + specify do + expect(checker.report).to have_attributes( + checker_name: 'EnumColumnChecker', + table_or_model_name: entity_class.name, + column_or_attribute_name: 'field', + status: :fail, + error_message: nil, + error_slug: :enum_column_type_mismatch + ) + end + end +end