consistency_fail 0.3.3 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +5 -13
  2. data/.rspec +1 -0
  3. data/bin/consistency_fail +1 -31
  4. data/consistency_fail.gemspec +2 -1
  5. data/lib/consistency_fail.rb +38 -0
  6. data/lib/consistency_fail/index.rb +18 -0
  7. data/lib/consistency_fail/introspectors/has_one.rb +1 -1
  8. data/lib/consistency_fail/introspectors/polymorphic.rb +1 -2
  9. data/lib/consistency_fail/introspectors/validates_uniqueness_of.rb +1 -0
  10. data/lib/consistency_fail/models.rb +0 -1
  11. data/lib/consistency_fail/version.rb +1 -1
  12. data/spec/index_spec.rb +37 -18
  13. data/spec/introspectors/has_one_spec.rb +14 -89
  14. data/spec/introspectors/polymorphic_spec.rb +15 -66
  15. data/spec/introspectors/table_data_spec.rb +47 -36
  16. data/spec/introspectors/validates_uniqueness_of_spec.rb +47 -72
  17. data/spec/models_spec.rb +2 -3
  18. data/spec/reporter_spec.rb +88 -49
  19. data/spec/spec_helper.rb +11 -10
  20. data/spec/support/active_record.rb +1 -0
  21. data/spec/support/models/correct_account.rb +3 -0
  22. data/spec/support/models/correct_address.rb +3 -0
  23. data/spec/support/models/correct_attachment.rb +5 -0
  24. data/spec/support/models/correct_person.rb +3 -0
  25. data/spec/support/models/correct_post.rb +3 -0
  26. data/spec/support/models/correct_user.rb +7 -0
  27. data/spec/support/models/correct_user/credential.rb +3 -0
  28. data/spec/support/models/correct_user/phone.rb +4 -0
  29. data/spec/support/models/new_correct_person.rb +7 -0
  30. data/spec/support/models/nonexistent.rb +2 -0
  31. data/spec/support/models/wrong_account.rb +3 -0
  32. data/spec/support/models/wrong_address.rb +5 -0
  33. data/spec/support/models/wrong_attachment.rb +3 -0
  34. data/spec/support/models/wrong_business.rb +3 -0
  35. data/spec/support/models/wrong_person.rb +3 -0
  36. data/spec/support/models/wrong_post.rb +3 -0
  37. data/spec/support/models/wrong_user.rb +3 -0
  38. data/spec/support/schema.rb +121 -0
  39. metadata +68 -24
@@ -1,52 +1,63 @@
1
- require 'spec_helper'
2
- require 'consistency_fail/introspectors/table_data'
3
-
4
1
  describe ConsistencyFail::Introspectors::TableData do
2
+
5
3
  describe "finding unique indexes" do
6
4
  it "finds none when the table does not exist" do
7
- model = fake_ar_model("User", :table_exists? => false)
8
-
9
- expect(subject.unique_indexes(model)).to eq([])
5
+ expect(subject.unique_indexes(Nonexistent)).to be_empty
10
6
  end
11
7
 
12
8
  it "gets one" do
13
- model = fake_ar_model("User", :table_exists? => true,
14
- :table_name => "users")
15
-
16
- allow(model).to receive_message_chain(:connection, :indexes).
17
- with("users").
18
- and_return([fake_index_on(["a"], :unique => true)])
19
-
20
- indexes = subject.unique_indexes(model)
21
- expect(indexes).to eq([ConsistencyFail::Index.new(double('model'), "users", ["a"])])
9
+ index = ConsistencyFail::Index.new(
10
+ CorrectAccount,
11
+ CorrectAccount.table_name,
12
+ ["email"]
13
+ )
14
+
15
+ expect(
16
+ ConsistencyFail::Introspectors::TableData.new.unique_indexes_by_table(
17
+ CorrectAccount,
18
+ ActiveRecord::Base.connection,
19
+ CorrectAccount.table_name
20
+ )
21
+ ).to eq [index]
22
22
  end
23
23
 
24
24
  it "doesn't get non-unique indexes" do
25
- model = fake_ar_model("User", :table_exists? => true,
26
- :table_name => "users")
27
-
28
- allow(model).to receive_message_chain(:connection, :indexes).
29
- with("users").
30
- and_return([fake_index_on(["a"], :unique => false)])
31
-
32
- expect(subject.unique_indexes(model)).to eq([])
25
+ expect(
26
+ ConsistencyFail::Introspectors::TableData.new.unique_indexes_by_table(
27
+ WrongAddress,
28
+ ActiveRecord::Base.connection,
29
+ WrongAddress.table_name
30
+ )
31
+ ).to be_empty
33
32
  end
34
33
 
35
34
  it "gets multiple unique indexes" do
36
- model = fake_ar_model("User", :table_exists? => true,
37
- :table_name => "users")
38
-
39
- allow(model).to receive_message_chain(:connection, :indexes).
40
- with("users").
41
- and_return([fake_index_on(["a"], :unique => true),
42
- fake_index_on(["b", "c"], :unique => true)])
43
-
44
- indexes = subject.unique_indexes(model)
45
- expect(indexes.size).to eq(2)
46
- expect(indexes).to eq([ConsistencyFail::Index.new(double('model'), "users", ["a"]),
47
- ConsistencyFail::Index.new(double('model'), "users", ["b", "c"])])
35
+ indexes = [
36
+ ConsistencyFail::Index.new(
37
+ CorrectAttachment,
38
+ CorrectAttachment.table_name,
39
+ ["name"]
40
+ ),
41
+ ConsistencyFail::Index.new(
42
+ CorrectAttachment,
43
+ CorrectAttachment.table_name,
44
+ ["attachable_id", "attachable_type"]
45
+ ),
46
+ ConsistencyFail::Index.new(
47
+ CorrectAttachment,
48
+ CorrectAttachment.table_name,
49
+ ["name", "attachable_id", "attachable_type"]
50
+ )
51
+ ]
52
+
53
+ expect(
54
+ ConsistencyFail::Introspectors::TableData.new.unique_indexes_by_table(
55
+ CorrectAttachment,
56
+ ActiveRecord::Base.connection,
57
+ CorrectAttachment.table_name
58
+ )
59
+ ).to eq indexes
48
60
  end
49
61
  end
50
62
 
51
63
  end
52
-
@@ -1,93 +1,68 @@
1
- require 'spec_helper'
2
- require 'consistency_fail/introspectors/validates_uniqueness_of'
3
-
4
1
  describe ConsistencyFail::Introspectors::ValidatesUniquenessOf do
5
- def introspector(model)
6
- ConsistencyFail::Introspectors::ValidatesUniquenessOf.new(model)
7
- end
8
-
9
- describe "instances of validates_uniqueness_of" do
10
- it "finds none" do
11
- model = fake_ar_model("User")
12
- allow(model).to receive(:validators).and_return([])
13
-
14
- expect(subject.instances(model)).to eq([])
15
- end
16
-
17
- it "finds one" do
18
- model = fake_ar_model("User")
19
- validation = double("validation", :class => ActiveRecord::Validations::UniquenessValidator)
20
- allow(model).to receive(:validators).and_return([validation])
21
-
22
- expect(subject.instances(model)).to eq([validation])
23
- end
24
-
25
- it "finds other validations, but not uniqueness" do
26
- model = fake_ar_model("User")
27
- validation = double("validation", :class => ActiveModel::Validations::FormatValidator)
28
- allow(model).to receive(:validators).and_return([validation])
29
-
30
- expect(subject.instances(model)).to eq([])
31
- end
32
- end
33
2
 
34
3
  describe "finding missing indexes" do
35
- before do
36
- @validation = double("validation", :class => ActiveRecord::Validations::UniquenessValidator)
37
- @model = fake_ar_model("User", :table_exists? => true,
38
- :table_name => "users",
39
- :validators => [@validation])
40
- end
41
-
42
4
  it "finds one" do
43
- allow(@validation).to receive_messages(:attributes => [:email], :options => {})
44
- allow(@model).to receive_message_chain(:connection, :indexes).with("users").and_return([])
45
-
46
- indexes = subject.missing_indexes(@model)
47
- expect(indexes).to eq([ConsistencyFail::Index.new(double('model'), "users", ["email"])])
5
+ indexes = subject.missing_indexes(WrongAccount)
6
+
7
+ expect(indexes).to eq([
8
+ ConsistencyFail::Index.new(
9
+ WrongAccount,
10
+ WrongAccount.table_name,
11
+ ["email"]
12
+ )
13
+ ])
48
14
  end
49
15
 
50
16
  it "finds one where the validation has scoped columns" do
51
- allow(@validation).to receive_messages(:attributes => [:city], :options => {:scope => [:email, :state]})
52
- allow(@model).to receive_message_chain(:connection, :indexes).with("users").and_return([])
53
-
54
- indexes = subject.missing_indexes(@model)
55
- expect(indexes).to eq([ConsistencyFail::Index.new(double('model'), "users", ["city", "email", "state"])])
56
- end
57
-
58
- it "leaves the columns in the given order" do
59
- allow(@validation).to receive_messages(:attributes => [:email], :options => {:scope => [:city, :state]})
60
- allow(@model).to receive_message_chain(:connection, :indexes).with("users").and_return([])
61
-
62
- indexes = subject.missing_indexes(@model)
63
- expect(indexes).to eq([ConsistencyFail::Index.new(double('model'), "users", ["email", "city", "state"])])
17
+ indexes = subject.missing_indexes(WrongBusiness)
18
+
19
+ expect(indexes).to eq([
20
+ ConsistencyFail::Index.new(
21
+ WrongBusiness,
22
+ WrongBusiness.table_name,
23
+ ["name", "city", "state"]
24
+ )
25
+ ])
64
26
  end
65
27
 
66
28
  it "finds two where there are multiple attributes" do
67
- allow(@validation).to receive_messages(:attributes => [:email, :name], :options => {:scope => [:city, :state]})
68
- allow(@model).to receive_message_chain(:connection, :indexes).with("users").and_return([])
69
-
70
- indexes = subject.missing_indexes(@model)
71
- expect(indexes).to eq([ConsistencyFail::Index.new(double('model'), "users", ["email", "city", "state"]),
72
- ConsistencyFail::Index.new(double('model'), "users", ["name", "city", "state"])])
29
+ indexes = subject.missing_indexes(WrongPerson)
30
+
31
+ expect(indexes).to eq(
32
+ [
33
+ ConsistencyFail::Index.new(
34
+ WrongPerson,
35
+ WrongPerson.table_name,
36
+ ["email", "city", "state"]
37
+ ),
38
+ ConsistencyFail::Index.new(
39
+ WrongPerson,
40
+ WrongPerson.table_name,
41
+ ["name", "city", "state"]
42
+ )
43
+ ]
44
+ )
73
45
  end
74
46
 
75
47
  it "finds none when they're already in place" do
76
- allow(@validation).to receive_messages(:attributes => [:email], :options => {})
77
- index = fake_index_on(["email"], :unique => true)
78
- allow(@model).to receive_message_chain(:connection, :indexes).with("users").
79
- and_return([index])
48
+ expect(subject.missing_indexes(CorrectAccount)).to be_empty
49
+ end
50
+
51
+ it "finds none even if scoped by association" do
52
+ expect(subject.missing_indexes(CorrectAddress)).to be_empty
53
+ end
80
54
 
81
- expect(subject.missing_indexes(@model)).to eq([])
55
+ it "finds none even if scoped by polymorphic association" do
56
+ expect(subject.missing_indexes(CorrectAttachment)).to be_empty
82
57
  end
83
58
 
84
59
  it "finds none when indexes are there but in a different order" do
85
- allow(@validation).to receive_messages(:attributes => [:email], :options => {:scope => [:city, :state]})
86
- index = fake_index_on(["state", "email", "city"], :unique => true)
87
- allow(@model).to receive_message_chain(:connection, :indexes).with("users").
88
- and_return([index])
60
+ expect(subject.missing_indexes(CorrectPerson)).to be_empty
61
+ end
89
62
 
90
- expect(subject.missing_indexes(@model)).to eq([])
63
+ it "finds none when the model is an SQL view" do
64
+ expect(subject.missing_indexes(NewCorrectPerson)).to be_empty
91
65
  end
92
66
  end
67
+
93
68
  end
@@ -1,7 +1,5 @@
1
- require 'spec_helper'
2
- require 'consistency_fail/models'
3
-
4
1
  describe ConsistencyFail::Models do
2
+
5
3
  def models(load_path)
6
4
  ConsistencyFail::Models.new(load_path)
7
5
  end
@@ -46,4 +44,5 @@ describe ConsistencyFail::Models do
46
44
 
47
45
  expect(models([]).all).to eq([model_a, model_c, model_b])
48
46
  end
47
+
49
48
  end
@@ -1,90 +1,129 @@
1
- require 'spec_helper'
2
- require 'consistency_fail/reporter'
3
- require 'consistency_fail/index'
4
-
5
1
  describe ConsistencyFail::Reporter do
6
- before(:each) do
7
- @real_out = $stdout
8
- @fake_out = StringIO.new
9
- $stdout = @fake_out
10
- end
11
- after(:each) do
12
- $stdout = @real_out
13
- end
14
-
2
+
15
3
  context "validates_uniqueness_of" do
16
4
  it "says everything's good" do
17
- subject.report_validates_uniqueness_problems([])
18
-
19
- expect(@fake_out.string).to match(/Hooray!/)
5
+ expect {
6
+ subject.report_validates_uniqueness_problems([])
7
+ }.to output(/Hooray!/).to_stdout
20
8
  end
21
9
 
22
10
  it "shows a missing single-column index on a single model" do
23
- missing_indexes = [ConsistencyFail::Index.new(double('model'), "users", ["email"])]
24
-
25
- subject.report_validates_uniqueness_problems(fake_ar_model("User", :table_name => "users") => missing_indexes)
11
+ missing_indexes = [
12
+ ConsistencyFail::Index.new(
13
+ WrongAccount,
14
+ WrongAccount.table_name,
15
+ ["email"]
16
+ )
17
+ ]
26
18
 
27
- expect(@fake_out.string).to match(/users\s+\(email\)/)
19
+ expect {
20
+ subject.report_validates_uniqueness_problems(
21
+ WrongAccount => missing_indexes
22
+ )
23
+ }.to output(/wrong_accounts\s+\(email\)/).to_stdout
28
24
  end
29
25
 
30
26
  it "shows a missing multiple-column index on a single model" do
31
- missing_indexes = [ConsistencyFail::Index.new(double('model'),"addresses", ["number", "street", "zip"])]
32
-
33
- subject.report_validates_uniqueness_problems(fake_ar_model("Address", :table_name => "addresses") => missing_indexes)
27
+ missing_indexes = [
28
+ ConsistencyFail::Index.new(
29
+ WrongBusiness,
30
+ WrongBusiness.table_name,
31
+ ["name", "city", "state"]
32
+ )
33
+ ]
34
34
 
35
- expect(@fake_out.string).to match(/addresses\s+\(number, street, zip\)/)
35
+ expect {
36
+ subject.report_validates_uniqueness_problems(
37
+ WrongBusiness => missing_indexes
38
+ )
39
+ }.to output(/wrong_businesses\s+\(name, city, state\)/).to_stdout
36
40
  end
37
41
 
38
42
  context "with problems on multiple models" do
39
- before(:each) do
40
- subject.report_validates_uniqueness_problems(
41
- fake_ar_model("User", :table_name => "users") =>
42
- [ConsistencyFail::Index.new(double('model'),"users", ["email"])],
43
- fake_ar_model("Citizen", :table_name => "citizens") =>
44
- [ConsistencyFail::Index.new(double('model'),"citizens", ["ssn"])]
45
- )
43
+ def report
44
+ missing_indices = {
45
+ WrongAccount => [
46
+ ConsistencyFail::Index.new(
47
+ WrongAccount,
48
+ WrongAccount.table_name,
49
+ ["email"]
50
+ )
51
+ ],
52
+ WrongBusiness => [
53
+ ConsistencyFail::Index.new(
54
+ WrongBusiness,
55
+ WrongBusiness.table_name,
56
+ ["name", "city", "state"]
57
+ )
58
+ ]
59
+ }
60
+
61
+ subject.report_validates_uniqueness_problems(missing_indices)
46
62
  end
47
63
 
48
64
  it "shows all problems" do
49
- expect(@fake_out.string).to match(/users\s+\(email\)/m)
50
- expect(@fake_out.string).to match(/citizens\s+\(ssn\)/m)
65
+ expect { report }.to output(/wrong_accounts\s+\(email\)/).to_stdout
66
+ expect { report }.to output(
67
+ /wrong_businesses\s+\(name, city, state\)/
68
+ ).to_stdout
51
69
  end
52
70
 
53
71
  it "orders the models alphabetically" do
54
- expect(@fake_out.string).to match(/citizens\s+\(ssn\).*users\s+\(email\)/m)
72
+ expect { report }.to output(/
73
+ wrong_accounts\s+\(email\)
74
+ (\s|\S)*
75
+ wrong_businesses\s+\(name,\scity,\sstate\)
76
+ /x).to_stdout
55
77
  end
56
78
  end
57
79
  end
58
80
 
59
81
  context "has_one" do
60
82
  it "says everything's good" do
61
- subject.report_has_one_problems([])
62
-
63
- expect(@fake_out.string).to match(/Hooray!/)
83
+ expect {
84
+ subject.report_has_one_problems([])
85
+ }.to output(/Hooray!/).to_stdout
64
86
  end
65
87
 
66
88
  it "shows a missing single-column index on a single model" do
67
- missing_indexes = [ConsistencyFail::Index.new(double('model'),"users", ["email"])]
68
-
69
- subject.report_has_one_problems(fake_ar_model("Friend", :table_name => "users") => missing_indexes)
89
+ missing_indexes = [
90
+ ConsistencyFail::Index.new(
91
+ WrongAddress,
92
+ WrongAddress.table_name,
93
+ ["wrong_user_id"]
94
+ )
95
+ ]
70
96
 
71
- expect(@fake_out.string).to match(/Friend\s+users\s+\(email\)/m)
97
+ expect {
98
+ subject.report_has_one_problems(WrongAddress => missing_indexes)
99
+ }.to output(/wrong_addresses\s+\(wrong_user_id\)/).to_stdout
72
100
  end
73
101
  end
74
102
 
75
103
  context "polymorphic" do
76
104
  it "says everything's good" do
77
- subject.report_polymorphic_problems([])
78
-
79
- expect(@fake_out.string).to match(/Hooray!/)
105
+ expect {
106
+ subject.report_polymorphic_problems([])
107
+ }.to output(/Hooray!/).to_stdout
80
108
  end
81
109
 
82
110
  it "shows a missing compound index on a single model" do
83
- missing_indexes = [ConsistencyFail::Index.new(double('model'), "addresses", ["addressable_type", "addressable_id"])]
84
-
85
- subject.report_polymorphic_problems(fake_ar_model("Address", :table_name => "addresses") => missing_indexes)
86
-
87
- expect(@fake_out.string).to match(/Address\s+addresses\s+\(addressable_type, addressable_id\)/m)
111
+ missing_indexes = [
112
+ ConsistencyFail::Index.new(
113
+ WrongAttachment,
114
+ WrongAttachment.table_name,
115
+ ["attachable_type", "attachable_id"]
116
+ )
117
+ ]
118
+
119
+ expect {
120
+ subject.report_polymorphic_problems(WrongAttachment => missing_indexes)
121
+ }.to(
122
+ output(
123
+ /wrong_attachments\s+\(attachable_type, attachable_id\)/
124
+ ).to_stdout
125
+ )
88
126
  end
89
127
  end
128
+
90
129
  end
@@ -1,13 +1,14 @@
1
- $:<< File.expand_path('..', __FILE__)
2
- $:<< File.expand_path('../../lib', __FILE__)
1
+ require 'bundler/setup'
3
2
 
4
- require 'rubygems'
3
+ Bundler.require(:default, :test)
5
4
 
6
- def fake_ar_model(name, options = {})
7
- double("AR model: #{name}", options.merge(:name => name))
8
- end
9
-
10
- def fake_index_on(columns, options = {})
11
- double("index on #{columns.inspect}", options.merge(:columns => columns))
12
- end
5
+ Dir['./spec/support/**/*.rb'].each { |file| require file }
13
6
 
7
+ RSpec.configure do |config|
8
+ config.around do |example|
9
+ ActiveRecord::Base.transaction do
10
+ example.run
11
+ raise ActiveRecord::Rollback
12
+ end
13
+ end
14
+ end