active_record_doctor 1.7.1 → 1.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +26 -9
  3. data/lib/active_record_doctor.rb +15 -1
  4. data/lib/active_record_doctor/printers/io_printer.rb +3 -3
  5. data/lib/active_record_doctor/tasks.rb +3 -0
  6. data/lib/active_record_doctor/tasks/base.rb +10 -2
  7. data/lib/active_record_doctor/tasks/extraneous_indexes.rb +2 -0
  8. data/lib/active_record_doctor/tasks/incorrect_boolean_presence_validation.rb +5 -1
  9. data/lib/active_record_doctor/tasks/missing_foreign_keys.rb +2 -0
  10. data/lib/active_record_doctor/tasks/missing_non_null_constraint.rb +5 -1
  11. data/lib/active_record_doctor/tasks/missing_presence_validation.rb +5 -1
  12. data/lib/active_record_doctor/tasks/missing_unique_indexes.rb +4 -3
  13. data/lib/active_record_doctor/tasks/undefined_table_references.rb +2 -0
  14. data/lib/active_record_doctor/tasks/unindexed_deleted_at.rb +6 -2
  15. data/lib/active_record_doctor/tasks/unindexed_foreign_keys.rb +2 -0
  16. data/lib/active_record_doctor/version.rb +1 -1
  17. data/lib/tasks/active_record_doctor.rake +1 -0
  18. data/test/active_record_doctor/printers/io_printer_test.rb +11 -3
  19. data/test/active_record_doctor/tasks/extraneous_indexes_test.rb +1 -5
  20. data/test/active_record_doctor/tasks/incorrect_boolean_presence_validation_test.rb +10 -5
  21. data/test/active_record_doctor/tasks/missing_foreign_keys_test.rb +1 -5
  22. data/test/active_record_doctor/tasks/missing_non_null_constraint_test.rb +10 -5
  23. data/test/active_record_doctor/tasks/missing_presence_validation_test.rb +10 -5
  24. data/test/active_record_doctor/tasks/missing_unique_indexes_test.rb +36 -5
  25. data/test/active_record_doctor/tasks/undefined_table_references_test.rb +1 -5
  26. data/test/active_record_doctor/tasks/unindexed_deleted_at_test.rb +30 -5
  27. data/test/active_record_doctor/tasks/unindexed_foreign_keys_test.rb +1 -5
  28. data/test/setup.rb +97 -0
  29. metadata +16 -104
  30. data/Rakefile +0 -28
  31. data/test/dummy/README.rdoc +0 -28
  32. data/test/dummy/Rakefile +0 -6
  33. data/test/dummy/app/assets/config/manifest.js +0 -1
  34. data/test/dummy/app/assets/javascripts/application.js +0 -13
  35. data/test/dummy/app/assets/stylesheets/application.css +0 -15
  36. data/test/dummy/app/controllers/application_controller.rb +0 -5
  37. data/test/dummy/app/helpers/application_helper.rb +0 -2
  38. data/test/dummy/app/models/application_record.rb +0 -3
  39. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  40. data/test/dummy/bin/bundle +0 -3
  41. data/test/dummy/bin/rails +0 -4
  42. data/test/dummy/bin/rake +0 -4
  43. data/test/dummy/bin/setup +0 -29
  44. data/test/dummy/config.ru +0 -4
  45. data/test/dummy/config/application.rb +0 -23
  46. data/test/dummy/config/boot.rb +0 -5
  47. data/test/dummy/config/database.yml +0 -19
  48. data/test/dummy/config/database.yml.travis +0 -5
  49. data/test/dummy/config/environment.rb +0 -5
  50. data/test/dummy/config/environments/development.rb +0 -41
  51. data/test/dummy/config/environments/production.rb +0 -79
  52. data/test/dummy/config/environments/test.rb +0 -47
  53. data/test/dummy/config/initializers/assets.rb +0 -11
  54. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  55. data/test/dummy/config/initializers/cookies_serializer.rb +0 -3
  56. data/test/dummy/config/initializers/filter_parameter_logging.rb +0 -4
  57. data/test/dummy/config/initializers/inflections.rb +0 -16
  58. data/test/dummy/config/initializers/mime_types.rb +0 -4
  59. data/test/dummy/config/initializers/session_store.rb +0 -3
  60. data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
  61. data/test/dummy/config/locales/en.yml +0 -23
  62. data/test/dummy/config/routes.rb +0 -56
  63. data/test/dummy/config/secrets.yml +0 -22
  64. data/test/dummy/db/migrate/base_migration.rb +0 -5
  65. data/test/dummy/db/schema.rb +0 -19
  66. data/test/dummy/log/development.log +0 -111
  67. data/test/dummy/log/test.log +0 -79424
  68. data/test/dummy/public/404.html +0 -67
  69. data/test/dummy/public/422.html +0 -67
  70. data/test/dummy/public/500.html +0 -66
  71. data/test/dummy/public/favicon.ico +0 -0
  72. data/test/support/assertions.rb +0 -11
  73. data/test/support/temping.rb +0 -25
  74. data/test/test_helper.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8d5091f9668515416b73453756daa5f78598f6d35f0f60073a947719e535110f
4
- data.tar.gz: 428450c43db5adb8510debfb9e895efba1f21649355255fe3eb7a2ea16c82642
3
+ metadata.gz: 649707d9a4a22831b3733778ad05614cc71f8edb704a68e9b18bd784a350daf9
4
+ data.tar.gz: 6e0fc8fe22ba882549152203c3a92f3eeebd3effb3f4eead6245001849b9e5de
5
5
  SHA512:
6
- metadata.gz: fffc1ffd284a2448ac5c74dae34a3bbe45eda4836b0e22a0af072ab6353f16b1386385254b1cdb2b87e4776372869d8cee67bf0a066f180b85bd45ef7ed3cc33
7
- data.tar.gz: 0011f60f1e37542911a72cce23cc8c8430310f8263fe7ba73aba30230ec8ff3f6e7ee9df72f42d8db6ff54e732a86aa49f1e6489a5b44d51bf1bd6c68821ebcd
6
+ metadata.gz: 437df56684da87b78b3336337cabe138f5375c54673a5d02da29b06fa9d238e1c9fa34a4cf9973b7bf944a0ef8ebbed5ec9dd9d9d12612f87e16597292ff22aa
7
+ data.tar.gz: 3ee83bab9d6dc5c88c4c47d33d69d30ecf373bb6ab4798accfc681066fd0a1a4580295f29a869c69a9ff582c925f7c2cb328b345429a92623942995a0416e1d6
data/README.md CHANGED
@@ -3,15 +3,15 @@
3
3
  Active Record Doctor helps to keep the database in a good shape. Currently, it
4
4
  can:
5
5
 
6
- * index unindexed foreign keys
7
- * detect extraneous indexes
8
- * detect unindexed `deleted_at` columns
9
- * detect missing foreign key constraints
10
- * detect models referencing undefined tables
11
- * detect uniqueness validations not backed by an unique index
12
- * detect missing non-`NULL` constraints
13
- * detect missing presence validations
14
- * detect incorrect presence validations on boolean columns
6
+ * index unindexed foreign keys - [`active_record_doctor:unindexed_foreign_keys`](#indexing-unindexed-foreign-keys)
7
+ * detect extraneous indexes - [`active_record_doctor:extraneous_indexes`](#removing-extraneous-indexes)
8
+ * detect unindexed `deleted_at` columns - [`active_record_doctor:unindexed_deleted_at`](#detecting-unindexed-deleted_at-columns)
9
+ * detect missing foreign key constraints - [`active_record_doctor:missing_foreign_keys`](#detecting-missing-foreign-key-constraints)
10
+ * detect models referencing undefined tables - [`active_record_doctor:undefined_table_references`](#detecting-models-referencing-undefined-tables)
11
+ * detect uniqueness validations not backed by an unique index - [`active_record_doctor:missing_unique_indexes`](#detecting-uniqueness-validations-not-backed-by-an-index)
12
+ * detect missing non-`NULL` constraints - [`active_record_doctor:missing_non_null_constraint`](#detecting-missing-non-null-constraints)
13
+ * detect missing presence validations - [`active_record_doctor:missing_presence_validation`](#detecting-missing-presence-validations)
14
+ * detect incorrect presence validations on boolean columns - [`active_record_doctor:incorrect_boolean_presence_validation`](#detecting-incorrect-presence-validations-on-boolean-columns)
15
15
 
16
16
  More features coming soon!
17
17
 
@@ -237,6 +237,8 @@ The following columns should be marked as `null: false`:
237
237
  You can mark the columns mentioned in the output as `null: false` by creating a
238
238
  migration and calling `change_column_null`.
239
239
 
240
+ This validator skips models whose corresponding database tables don't exist.
241
+
240
242
  ### Detecting Missing Presence Validations
241
243
 
242
244
  If a column is marked as `null: false` then it's likely it should have the
@@ -257,6 +259,8 @@ The following models and columns should have presence validations:
257
259
 
258
260
  This means `User` should have a presence validator on `email` and `name`.
259
261
 
262
+ This validator skips models whose corresponding database tables don't exist.
263
+
260
264
  ### Detecting Incorrect Presence Validations on Boolean Columns
261
265
 
262
266
  A boolean column's presence should be validated using inclusion or exclusion
@@ -278,6 +282,19 @@ The presence of the following boolean columns is validated incorrectly:
278
282
  This means `active` is validated with `presence: true` instead of
279
283
  `inclusion: { in: [true, false] }` or `exclusion: { in: [nil] }`.
280
284
 
285
+ This validator skips models whose corresponding database tables don't exist.
286
+
287
+ ## Ruby and Rails Compatibility Policy
288
+
289
+ The goal of the policy is to ensure proper functioning in reasonable
290
+ combinations of Ruby and Rails versions. Specifically:
291
+
292
+ 1. If a Rails version is officially supported by the Rails Core Team then it's
293
+ supported by `active_record_doctor`.
294
+ 2. If a Ruby version is compatible with a supported Rails version then it's
295
+ also supported by `active_record_doctor`.
296
+ 3. Only most recent teeny Ruby versions and patch Rails versions are supported.
297
+
281
298
  ## Author
282
299
 
283
300
  This gem is developed and maintained by [Greg Navis](http://www.gregnavis.com).
@@ -1,4 +1,18 @@
1
- require "active_record_doctor/railtie" if defined?(Rails)
1
+ require "active_record_doctor/printers"
2
+ require "active_record_doctor/printers/io_printer"
3
+ require "active_record_doctor/railtie" if defined?(Rails) && defined?(Rails::Railtie)
4
+ require "active_record_doctor/tasks"
5
+ require "active_record_doctor/tasks/base"
6
+ require "active_record_doctor/tasks/missing_presence_validation"
7
+ require "active_record_doctor/tasks/missing_foreign_keys"
8
+ require "active_record_doctor/tasks/missing_unique_indexes"
9
+ require "active_record_doctor/tasks/incorrect_boolean_presence_validation"
10
+ require "active_record_doctor/tasks/extraneous_indexes"
11
+ require "active_record_doctor/tasks/unindexed_deleted_at"
12
+ require "active_record_doctor/tasks/undefined_table_references"
13
+ require "active_record_doctor/tasks/missing_non_null_constraint"
14
+ require "active_record_doctor/tasks/unindexed_foreign_keys"
15
+ require "active_record_doctor/version"
2
16
 
3
17
  module ActiveRecordDoctor
4
18
  end
@@ -92,11 +92,11 @@ EOS
92
92
  end
93
93
  end
94
94
 
95
- def presence_true_on_boolean(presence_true_on_booleans)
96
- return if presence_true_on_booleans.empty?
95
+ def incorrect_boolean_presence_validation(incorrect_boolean_presence_validations)
96
+ return if incorrect_boolean_presence_validations.empty?
97
97
 
98
98
  @io.puts('The presence of the following boolean columns is validated incorrectly:')
99
- presence_true_on_booleans.each do |table, columns|
99
+ incorrect_boolean_presence_validations.each do |table, columns|
100
100
  @io.puts(" #{table}: #{columns.join(', ')}")
101
101
  end
102
102
  end
@@ -1,3 +1,6 @@
1
+ require "active_support"
2
+ require "active_support/core_ext/class/subclasses"
3
+
1
4
  module ActiveRecordDoctor
2
5
  module Tasks
3
6
  def self.all
@@ -7,6 +7,10 @@ module ActiveRecordDoctor
7
7
  new.run
8
8
  end
9
9
 
10
+ def self.description
11
+ @description
12
+ end
13
+
10
14
  def initialize(printer = ActiveRecordDoctor::Printers::IOPrinter.new)
11
15
  @printer = printer
12
16
  end
@@ -27,16 +31,20 @@ module ActiveRecordDoctor
27
31
 
28
32
  def tables
29
33
  @tables ||=
30
- if Rails::VERSION::MAJOR == 5
34
+ if ActiveRecord::VERSION::MAJOR == 5
31
35
  connection.data_sources
32
36
  else
33
37
  connection.tables
34
38
  end
35
39
  end
36
40
 
41
+ def table_exists?(table_name)
42
+ connection.table_exists?(table_name)
43
+ end
44
+
37
45
  def views
38
46
  @views ||=
39
- if Rails::VERSION::MAJOR == 5
47
+ if ActiveRecord::VERSION::MAJOR == 5
40
48
  connection.views
41
49
  elsif connection.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
42
50
  ActiveRecord::Base.connection.execute(<<-SQL).map { |tuple| tuple.fetch("relname") }
@@ -3,6 +3,8 @@ require "active_record_doctor/tasks/base"
3
3
  module ActiveRecordDoctor
4
4
  module Tasks
5
5
  class ExtraneousIndexes < Base
6
+ @description = 'Detect extraneous indexes'
7
+
6
8
  def run
7
9
  success(subindexes_of_multi_column_indexes + indexed_primary_keys)
8
10
  end
@@ -3,11 +3,15 @@ require "active_record_doctor/tasks/base"
3
3
  module ActiveRecordDoctor
4
4
  module Tasks
5
5
  class IncorrectBooleanPresenceValidation < Base
6
+ @description = 'Detect boolean columns with presence/absence instead of includes/excludes validators'
7
+
6
8
  def run
7
9
  eager_load!
8
10
 
9
11
  success(hash_from_pairs(models.reject do |model|
10
- model.table_name.nil? || model.table_name == 'schema_migrations'
12
+ model.table_name.nil? ||
13
+ model.table_name == 'schema_migrations' ||
14
+ !table_exists?(model.table_name)
11
15
  end.map do |model|
12
16
  [
13
17
  model.name,
@@ -3,6 +3,8 @@ require "active_record_doctor/tasks/base"
3
3
  module ActiveRecordDoctor
4
4
  module Tasks
5
5
  class MissingForeignKeys < Base
6
+ @description = 'Detect association columns without a foreign key constraint'
7
+
6
8
  def run
7
9
  success(hash_from_pairs(tables.select do |table|
8
10
  "schema_migrations" != table
@@ -3,11 +3,15 @@ require "active_record_doctor/tasks/base"
3
3
  module ActiveRecordDoctor
4
4
  module Tasks
5
5
  class MissingNonNullConstraint < Base
6
+ @description = 'Detect presence validators not backed by a non-NULL constraint'
7
+
6
8
  def run
7
9
  eager_load!
8
10
 
9
11
  success(hash_from_pairs(models.reject do |model|
10
- model.table_name.nil? || model.table_name == 'schema_migrations'
12
+ model.table_name.nil? ||
13
+ model.table_name == 'schema_migrations' ||
14
+ !table_exists?(model.table_name)
11
15
  end.map do |model|
12
16
  [
13
17
  model.table_name,
@@ -3,11 +3,15 @@ require "active_record_doctor/tasks/base"
3
3
  module ActiveRecordDoctor
4
4
  module Tasks
5
5
  class MissingPresenceValidation < Base
6
+ @description = 'Detect non-NULL columns without a presence validator'
7
+
6
8
  def run
7
9
  eager_load!
8
10
 
9
11
  success(hash_from_pairs(models.reject do |model|
10
- model.table_name.nil? || model.table_name == 'schema_migrations'
12
+ model.table_name.nil? ||
13
+ model.table_name == 'schema_migrations' ||
14
+ !table_exists?(model.table_name)
11
15
  end.map do |model|
12
16
  [
13
17
  model.name,
@@ -3,6 +3,8 @@ require "active_record_doctor/tasks/base"
3
3
  module ActiveRecordDoctor
4
4
  module Tasks
5
5
  class MissingUniqueIndexes < Base
6
+ @description = 'Detect columns covered by a uniqueness validator without a unique index'
7
+
6
8
  def run
7
9
  eager_load!
8
10
 
@@ -13,12 +15,11 @@ module ActiveRecordDoctor
13
15
  model.table_name,
14
16
  model.validators.select do |validator|
15
17
  table_name = model.table_name
16
- attributes = validator.attributes
17
18
  scope = validator.options.fetch(:scope, [])
18
19
 
19
20
  validator.is_a?(ActiveRecord::Validations::UniquenessValidator) &&
20
21
  supported_validator?(validator) &&
21
- !unique_index?(table_name, attributes, scope)
22
+ !unique_index?(table_name, validator.attributes, scope)
22
23
  end.map do |validator|
23
24
  scope = Array(validator.options.fetch(:scope, []))
24
25
  attributes = validator.attributes
@@ -48,7 +49,7 @@ module ActiveRecordDoctor
48
49
  columns = (Array(scope) + columns).map(&:to_s)
49
50
 
50
51
  indexes(table_name).any? do |index|
51
- index.columns == columns && index.unique
52
+ index.columns.to_set == columns.to_set && index.unique
52
53
  end
53
54
  end
54
55
  end
@@ -3,6 +3,8 @@ require "active_record_doctor/tasks/base"
3
3
  module ActiveRecordDoctor
4
4
  module Tasks
5
5
  class UndefinedTableReferences < Base
6
+ @description = 'Detect models referencing undefined tables or views'
7
+
6
8
  def run
7
9
  eager_load!
8
10
 
@@ -3,12 +3,16 @@ require "active_record_doctor/tasks/base"
3
3
  module ActiveRecordDoctor
4
4
  module Tasks
5
5
  class UnindexedDeletedAt < Base
6
+ COLUMNS = %w[deleted_at discarded_at].freeze
7
+ PATTERN = COLUMNS.join('|').freeze
8
+ @description = 'Detect unindexed deleted_at columns'
9
+
6
10
  def run
7
11
  success(connection.tables.select do |table|
8
- connection.columns(table).map(&:name).include?('deleted_at')
12
+ connection.columns(table).any? { |column| column.name =~ /^#{PATTERN}$/ }
9
13
  end.flat_map do |table|
10
14
  connection.indexes(table).reject do |index|
11
- index.where =~ /\bdeleted_at\s+IS\s+NULL\b/i
15
+ index.where =~ /\b#{PATTERN}\s+IS\s+NULL\b/i
12
16
  end.map do |index|
13
17
  index.name
14
18
  end
@@ -3,6 +3,8 @@ require "active_record_doctor/tasks/base"
3
3
  module ActiveRecordDoctor
4
4
  module Tasks
5
5
  class UnindexedForeignKeys < Base
6
+ @description = 'Detect foreign keys without an index on them'
7
+
6
8
  def run
7
9
  success(hash_from_pairs(tables.select do |table|
8
10
  "schema_migrations" != table
@@ -1,3 +1,3 @@
1
1
  module ActiveRecordDoctor
2
- VERSION = "1.7.1"
2
+ VERSION = "1.7.2"
3
3
  end
@@ -13,6 +13,7 @@ namespace :active_record_doctor do
13
13
  def mount(task_class)
14
14
  name = task_class.name.demodulize.underscore.to_sym
15
15
 
16
+ desc task_class.description
16
17
  task name => :environment do
17
18
  result, success = task_class.run
18
19
  success = true if success.nil?
@@ -1,8 +1,16 @@
1
- require 'test_helper'
1
+ # Load all tasks
2
+ class ActiveRecordDoctor::Printers::IOPrinterTest < Minitest::Test
3
+ def test_all_tasks_have_printers
4
+ ActiveRecordDoctor::Tasks::Base.subclasses.each do |task_class|
5
+ name = task_class.name.demodulize.underscore.to_sym
2
6
 
3
- require 'active_record_doctor/printers/io_printer'
7
+ assert(
8
+ ActiveRecordDoctor::Printers::IOPrinter.method_defined?(name),
9
+ "IOPrinter should define #{name}"
10
+ )
11
+ end
12
+ end
4
13
 
5
- class ActiveRecordDoctor::Printers::IOPrinterTest < ActiveSupport::TestCase
6
14
  def test_unindexed_foreign_keys
7
15
  assert_equal(<<EOF, unindexed_foreign_keys({ "users" => ["profile_id", "account_id"], "account" => ["group_id"] }))
8
16
  account group_id
@@ -1,8 +1,4 @@
1
- require 'test_helper'
2
-
3
- require 'active_record_doctor/tasks/extraneous_indexes'
4
-
5
- class ActiveRecordDoctor::Tasks::ExtraneousIndexesTest < ActiveSupport::TestCase
1
+ class ActiveRecordDoctor::Tasks::ExtraneousIndexesTest < Minitest::Test
6
2
  def test_index_on_primary_key_is_duplicate
7
3
  Temping.create(:user, temporary: false) do
8
4
  with_columns do |t|
@@ -1,8 +1,4 @@
1
- require 'test_helper'
2
-
3
- require 'active_record_doctor/tasks/incorrect_boolean_presence_validation'
4
-
5
- class ActiveRecordDoctor::Tasks::IncorrectBooleanPresenceValidationTest < ActiveSupport::TestCase
1
+ class ActiveRecordDoctor::Tasks::IncorrectBooleanPresenceValidationTest < Minitest::Test
6
2
  def test_presence_true_is_reported_on_boolean_only
7
3
  Temping.create(:users, temporary: false) do
8
4
  # email is a non-boolean column whose presence CAN be validated in the
@@ -30,4 +26,13 @@ class ActiveRecordDoctor::Tasks::IncorrectBooleanPresenceValidationTest < Active
30
26
 
31
27
  assert_equal({}, run_task)
32
28
  end
29
+
30
+ def test_models_with_non_existent_tables_are_skipped
31
+ klass = Class.new(ActiveRecord::Base) do
32
+ self.table_name = 'action_text_rich_texts'
33
+ end
34
+
35
+ # No need to assert anything as merely not raising an exception is a success.
36
+ run_task
37
+ end
33
38
  end
@@ -1,8 +1,4 @@
1
- require 'test_helper'
2
-
3
- require 'active_record_doctor/tasks/missing_foreign_keys'
4
-
5
- class ActiveRecordDoctor::Tasks::MissingForeignKeysTest < ActiveSupport::TestCase
1
+ class ActiveRecordDoctor::Tasks::MissingForeignKeysTest < Minitest::Test
6
2
  def test_missing_foreign_key_is_reported
7
3
  Temping.create(:companies, temporary: false)
8
4
  Temping.create(:users, temporary: false) do
@@ -1,8 +1,4 @@
1
- require 'test_helper'
2
-
3
- require 'active_record_doctor/tasks/missing_non_null_constraint'
4
-
5
- class ActiveRecordDoctor::Tasks::MissingNonNullConstraintTest < ActiveSupport::TestCase
1
+ class ActiveRecordDoctor::Tasks::MissingNonNullConstraintTest < Minitest::Test
6
2
  def test_presence_true_and_null_true
7
3
  Temping.create(:users, temporary: false) do
8
4
  validates :name, presence: true
@@ -105,4 +101,13 @@ class ActiveRecordDoctor::Tasks::MissingNonNullConstraintTest < ActiveSupport::T
105
101
 
106
102
  assert_equal({}, run_task)
107
103
  end
104
+
105
+ def test_models_with_non_existent_tables_are_skipped
106
+ klass = Class.new(ActiveRecord::Base) do
107
+ self.table_name = 'action_text_rich_texts'
108
+ end
109
+
110
+ # No need to assert anything as merely not raising an exception is a success.
111
+ run_task
112
+ end
108
113
  end
@@ -1,8 +1,4 @@
1
- require 'test_helper'
2
-
3
- require 'active_record_doctor/tasks/missing_presence_validation'
4
-
5
- class ActiveRecordDoctor::Tasks::MissingPresenceValidationTest < ActiveSupport::TestCase
1
+ class ActiveRecordDoctor::Tasks::MissingPresenceValidationTest < Minitest::Test
6
2
  def test_null_column_is_not_reported_if_validation_absent
7
3
  Temping.create(:users, temporary: false) do
8
4
  with_columns do |t|
@@ -107,4 +103,13 @@ class ActiveRecordDoctor::Tasks::MissingPresenceValidationTest < ActiveSupport::
107
103
 
108
104
  assert_equal({}, run_task)
109
105
  end
106
+
107
+ def test_models_with_non_existent_tables_are_skipped
108
+ klass = Class.new(ActiveRecord::Base) do
109
+ self.table_name = 'action_text_rich_texts'
110
+ end
111
+
112
+ # No need to assert anything as merely not raising an exception is a success.
113
+ run_task
114
+ end
110
115
  end
@@ -1,8 +1,4 @@
1
- require 'test_helper'
2
-
3
- require 'active_record_doctor/tasks/missing_unique_indexes'
4
-
5
- class ActiveRecordDoctor::Tasks::MissingUniqueIndexesTest < ActiveSupport::TestCase
1
+ class ActiveRecordDoctor::Tasks::MissingUniqueIndexesTest < Minitest::Test
6
2
  def test_missing_unique_index
7
3
  Temping.create(:users, temporary: false) do
8
4
  with_columns do |t|
@@ -63,6 +59,21 @@ class ActiveRecordDoctor::Tasks::MissingUniqueIndexesTest < ActiveSupport::TestC
63
59
  assert_result([])
64
60
  end
65
61
 
62
+ def test_column_order_is_ignored
63
+ Temping.create(:users, temporary: false) do
64
+ with_columns do |t|
65
+ t.string :email
66
+ t.integer :organization_id
67
+
68
+ t.index [:email, :organization_id], unique: true
69
+ end
70
+
71
+ validates :email, uniqueness: { scope: :organization_id }
72
+ end
73
+
74
+ assert_result([])
75
+ end
76
+
66
77
  def test_conditions_is_skipped
67
78
  assert_skipped(conditions: -> { where.not(email: nil) })
68
79
  end
@@ -79,6 +90,26 @@ class ActiveRecordDoctor::Tasks::MissingUniqueIndexesTest < ActiveSupport::TestC
79
90
  assert_skipped(unless: ->(model) { true })
80
91
  end
81
92
 
93
+ def test_skips_validator_without_attributes
94
+ Temping.create(:users, temporary: false) do
95
+ with_columns do |t|
96
+ t.string :email
97
+ t.index :email
98
+ end
99
+
100
+ validates_with DummyValidator
101
+ end
102
+
103
+ # There's no need for assert/refute as it's enough the line below doesn't
104
+ # raise an exception.
105
+ run_task
106
+ end
107
+
108
+ class DummyValidator < ActiveModel::Validator
109
+ def validate(record)
110
+ end
111
+ end
112
+
82
113
  private
83
114
 
84
115
  def assert_skipped(options)