From e1fc2208ebb321048b0b78ce5af13af81673b71e Mon Sep 17 00:00:00 2001 From: Sergey Toy Date: Tue, 11 Apr 2023 18:24:45 +0800 Subject: [PATCH 1/6] implement three state boolean checker and tests --- lib/database_consistency.rb | 1 + .../three_state_boolean_checker.rb | 44 +++++++++ .../processors/columns_processor.rb | 3 +- .../three_state_boolean_checker_spec.rb | 93 +++++++++++++++++++ 4 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb create mode 100644 spec/checkers/three_state_boolean_checker_spec.rb diff --git a/lib/database_consistency.rb b/lib/database_consistency.rb index a2f485c..a5a6aaf 100644 --- a/lib/database_consistency.rb +++ b/lib/database_consistency.rb @@ -71,6 +71,7 @@ require 'database_consistency/checkers/column_checkers/length_constraint_checker' require 'database_consistency/checkers/column_checkers/primary_key_type_checker' require 'database_consistency/checkers/column_checkers/enum_value_checker' +require 'database_consistency/checkers/column_checkers/three_state_boolean_checker' require 'database_consistency/checkers/validator_checkers/validator_checker' require 'database_consistency/checkers/validator_checkers/missing_unique_index_checker' diff --git a/lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb b/lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb new file mode 100644 index 0000000..9fe9a63 --- /dev/null +++ b/lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module DatabaseConsistency + module Checkers + # This class checks missing presence validator + class ThreeStateBooleanChecker < ColumnChecker + Report = ReportBuilder.define( + DatabaseConsistency::Report, + :table_name, + :column_name + ) + + private + + def preconditions + column.type == :boolean + end + + def check + if valid? + report_template(:ok) + else + report_template(:fail, error_slug: :three_state_boolean) + 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 + + # @return [Boolean] + def valid? + !column.null && !column.default.nil? + end + end + end +end diff --git a/lib/database_consistency/processors/columns_processor.rb b/lib/database_consistency/processors/columns_processor.rb index e2b483a..e033384 100644 --- a/lib/database_consistency/processors/columns_processor.rb +++ b/lib/database_consistency/processors/columns_processor.rb @@ -8,7 +8,8 @@ class ColumnsProcessor < BaseProcessor Checkers::NullConstraintChecker, Checkers::LengthConstraintChecker, Checkers::PrimaryKeyTypeChecker, - Checkers::EnumValueChecker + Checkers::EnumValueChecker, + Checkers::ThreeStateBooleanChecker ].freeze private diff --git a/spec/checkers/three_state_boolean_checker_spec.rb b/spec/checkers/three_state_boolean_checker_spec.rb new file mode 100644 index 0000000..140b5d6 --- /dev/null +++ b/spec/checkers/three_state_boolean_checker_spec.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +RSpec.describe DatabaseConsistency::Checkers::ThreeStateBooleanChecker, :sqlite, :mysql, :postgresql do + subject(:checker) { described_class.new(model, column) } + + let(:klass) { define_class } + let(:model) { klass } + let(:column) { klass.columns.first } + + context 'when column is nullable and without default' do + before do + define_database_with_entity do |table| + table.boolean :active + end + end + + specify do + expect(checker.report).to have_attributes( + checker_name: 'ThreeStateBooleanChecker', + table_or_model_name: klass.name, + column_or_attribute_name: 'active', + status: :fail, + error_message: nil, + error_slug: :three_state_boolean, + table_name: klass.table_name, + column_name: 'active' + ) + end + end + + context 'when column is not nullable and without default' do + before do + define_database_with_entity do |table| + table.boolean :active, null: false + end + end + + specify do + expect(checker.report).to have_attributes( + checker_name: 'ThreeStateBooleanChecker', + table_or_model_name: klass.name, + column_or_attribute_name: 'active', + status: :fail, + error_message: nil, + error_slug: :three_state_boolean, + table_name: klass.table_name, + column_name: 'active' + ) + end + end + + context 'when column is nullable and with default' do + before do + define_database_with_entity do |table| + table.boolean :active, default: true + end + end + + specify do + expect(checker.report).to have_attributes( + checker_name: 'ThreeStateBooleanChecker', + table_or_model_name: klass.name, + column_or_attribute_name: 'active', + status: :fail, + error_message: nil, + error_slug: :three_state_boolean, + table_name: klass.table_name, + column_name: 'active' + ) + end + end + + context 'when column is not nullable and with default' do + before do + define_database_with_entity do |table| + table.boolean :active, null: false, default: true + end + end + + specify do + expect(checker.report).to have_attributes( + checker_name: 'ThreeStateBooleanChecker', + table_or_model_name: klass.name, + column_or_attribute_name: 'active', + status: :ok, + error_message: nil, + error_slug: nil, + table_name: klass.table_name, + column_name: 'active' + ) + end + end +end From 89d2f081899a8b590f1b8076764fcf1f5d89b3af Mon Sep 17 00:00:00 2001 From: Sergey Toy Date: Tue, 11 Apr 2023 18:54:43 +0800 Subject: [PATCH 2/6] fix comment --- .../checkers/column_checkers/three_state_boolean_checker.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb b/lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb index 9fe9a63..0c8cc49 100644 --- a/lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb +++ b/lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb @@ -2,7 +2,7 @@ module DatabaseConsistency module Checkers - # This class checks missing presence validator + # This class checks missing NOT NULL constraint and default value for boolean columns class ThreeStateBooleanChecker < ColumnChecker Report = ReportBuilder.define( DatabaseConsistency::Report, From a03a46924d2ecfb186229e2c17db20c8f8ddce3a Mon Sep 17 00:00:00 2001 From: Sergey Toy Date: Sun, 16 Apr 2023 20:31:14 +0800 Subject: [PATCH 3/6] enforce `NOT NULL` only --- .../checkers/column_checkers/three_state_boolean_checker.rb | 2 +- spec/checkers/three_state_boolean_checker_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb b/lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb index 0c8cc49..6ad2ab4 100644 --- a/lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb +++ b/lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb @@ -37,7 +37,7 @@ def report_template(status, error_slug: nil) # @return [Boolean] def valid? - !column.null && !column.default.nil? + !column.null end end end diff --git a/spec/checkers/three_state_boolean_checker_spec.rb b/spec/checkers/three_state_boolean_checker_spec.rb index 140b5d6..d47224f 100644 --- a/spec/checkers/three_state_boolean_checker_spec.rb +++ b/spec/checkers/three_state_boolean_checker_spec.rb @@ -40,9 +40,9 @@ checker_name: 'ThreeStateBooleanChecker', table_or_model_name: klass.name, column_or_attribute_name: 'active', - status: :fail, + status: :ok, error_message: nil, - error_slug: :three_state_boolean, + error_slug: nil, table_name: klass.table_name, column_name: 'active' ) From e0779a87f319ea42d4719a63f912697f5b4a0a32 Mon Sep 17 00:00:00 2001 From: Sergey Toy Date: Sun, 16 Apr 2023 20:32:48 +0800 Subject: [PATCH 4/6] enable autofix --- lib/database_consistency/writers/autofix_writer.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/database_consistency/writers/autofix_writer.rb b/lib/database_consistency/writers/autofix_writer.rb index 12a2089..dc54851 100644 --- a/lib/database_consistency/writers/autofix_writer.rb +++ b/lib/database_consistency/writers/autofix_writer.rb @@ -16,7 +16,8 @@ class AutofixWriter < BaseWriter null_constraint_missing: Autofix::NullConstraintMissing, redundant_index: Autofix::RedundantIndex, redundant_unique_index: Autofix::RedundantIndex, - small_primary_key: Autofix::InconsistentTypes + small_primary_key: Autofix::InconsistentTypes, + three_state_boolean: Autofix::NullConstraintMissing }.freeze def write From 001c329a32fd81da45b7a1916fe83465b7cda867 Mon Sep 17 00:00:00 2001 From: Sergey Toy Date: Sun, 16 Apr 2023 20:41:34 +0800 Subject: [PATCH 5/6] fix description --- .../checkers/column_checkers/three_state_boolean_checker.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb b/lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb index 6ad2ab4..0615f58 100644 --- a/lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb +++ b/lib/database_consistency/checkers/column_checkers/three_state_boolean_checker.rb @@ -2,7 +2,7 @@ module DatabaseConsistency module Checkers - # This class checks missing NOT NULL constraint and default value for boolean columns + # This class checks missing NOT NULL constraint for boolean columns class ThreeStateBooleanChecker < ColumnChecker Report = ReportBuilder.define( DatabaseConsistency::Report, From 9027c1e05ce88e20b1d1df50e9a465dc5f9161b1 Mon Sep 17 00:00:00 2001 From: Sergey Toy Date: Sun, 16 Apr 2023 20:58:54 +0800 Subject: [PATCH 6/6] add writer --- lib/database_consistency.rb | 1 + .../writers/simple/three_state_boolean.rb | 22 +++++++++++++++++++ .../writers/simple_writer.rb | 3 ++- 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 lib/database_consistency/writers/simple/three_state_boolean.rb diff --git a/lib/database_consistency.rb b/lib/database_consistency.rb index a5a6aaf..4a82d8d 100644 --- a/lib/database_consistency.rb +++ b/lib/database_consistency.rb @@ -38,6 +38,7 @@ require 'database_consistency/writers/simple/enum_values_inconsistent_with_ar_enum' 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_writer' require 'database_consistency/writers/autofix/helpers/migration' diff --git a/lib/database_consistency/writers/simple/three_state_boolean.rb b/lib/database_consistency/writers/simple/three_state_boolean.rb new file mode 100644 index 0000000..895caf0 --- /dev/null +++ b/lib/database_consistency/writers/simple/three_state_boolean.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module DatabaseConsistency + module Writers + module Simple + class ThreeStateBoolean < Base # :nodoc: + private + + def template + 'boolean column should have NOT NULL constraint' + 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 091ef30..96dd783 100644 --- a/lib/database_consistency/writers/simple_writer.rb +++ b/lib/database_consistency/writers/simple_writer.rb @@ -28,7 +28,8 @@ class SimpleWriter < BaseWriter missing_foreign_key_cascade: Simple::MissingForeignKeyCascade, enum_values_inconsistent_with_ar_enum: Simple::EnumValuesInconsistentWithArEnum, enum_values_inconsistent_with_inclusion: Simple::EnumValuesInconsistentWithInclusion, - redundant_case_insensitive_option: Simple::RedundantCaseInsensitiveOption + redundant_case_insensitive_option: Simple::RedundantCaseInsensitiveOption, + three_state_boolean: Simple::ThreeStateBoolean }.freeze def write