schema_expectations 0.0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.travis.yml +19 -5
  4. data/Appraisals +33 -0
  5. data/CHANGELOG.md +17 -1
  6. data/README.md +4 -0
  7. data/Rakefile +12 -0
  8. data/gemfiles/activerecord_3.1.gemfile +7 -0
  9. data/gemfiles/activerecord_3.2.gemfile +7 -0
  10. data/gemfiles/activerecord_4.0.gemfile +7 -0
  11. data/gemfiles/activerecord_4.1.gemfile +7 -0
  12. data/gemfiles/activerecord_4.2.gemfile +7 -0
  13. data/gemfiles/default.gemfile +5 -0
  14. data/gemfiles/rspec_3.0.gemfile +7 -0
  15. data/gemfiles/rspec_3.1.gemfile +7 -0
  16. data/gemfiles/rspec_3.2.gemfile +7 -0
  17. data/lib/schema_expectations/active_record/column_reflector.rb +92 -0
  18. data/lib/schema_expectations/active_record/validation_reflector.rb +59 -0
  19. data/lib/schema_expectations/config.rb +25 -0
  20. data/lib/schema_expectations/rspec_matchers/validate_schema_nullable.rb +49 -58
  21. data/lib/schema_expectations/util.rb +9 -0
  22. data/lib/schema_expectations/version.rb +1 -1
  23. data/lib/schema_expectations.rb +1 -0
  24. data/schema_expectations.gemspec +12 -3
  25. data/spec/db/database.yml +13 -0
  26. data/spec/lib/schema_expectations/active_record/column_reflector_spec.rb +150 -0
  27. data/spec/lib/schema_expectations/active_record/validation_reflector_spec.rb +62 -0
  28. data/spec/lib/schema_expectations/config_spec.rb +22 -0
  29. data/spec/lib/schema_expectations/rspec_matchers/validate_schema_nullable_spec.rb +260 -77
  30. data/spec/lib/schema_expectations/util_spec.rb +16 -0
  31. data/spec/meta_spec.rb +44 -0
  32. data/spec/spec_helper.rb +11 -2
  33. data/spec/support/active_record.rb +34 -12
  34. data/spec/support/active_record_helpers.rb +51 -0
  35. data/spec/support/gem_filters.rb +6 -0
  36. metadata +162 -10
@@ -0,0 +1,150 @@
1
+ require 'spec_helper'
2
+ require 'schema_expectations/active_record/column_reflector'
3
+
4
+ module SchemaExpectations
5
+ module ActiveRecord
6
+ describe ColumnReflector, :active_record do
7
+ subject(:column_reflector) { ColumnReflector.new(Record) }
8
+
9
+ context '#without_present_default' do
10
+ specify 'filters default values' do
11
+ create_table :records do |t|
12
+ t.integer :integer_default, default: 0
13
+ t.string :string_default, default: 'test'
14
+ t.string :empty_default, default: ''
15
+ t.string :null_default, default: nil
16
+ t.string :no_default
17
+
18
+ t.timestamps null: false
19
+ end
20
+ stub_const('Record', Class.new(::ActiveRecord::Base))
21
+
22
+ expect(column_reflector.column_names).
23
+ to eq %i(id integer_default string_default empty_default null_default no_default created_at updated_at)
24
+
25
+ expect(column_reflector.without_present_default.column_names).
26
+ to eq %i(empty_default null_default no_default)
27
+ end
28
+
29
+ context 'default functions', :postgresql, active_record_version: '>= 4.0' do
30
+ specify 'are filtered' do
31
+ create_table :records do |t|
32
+ t.uuid :uuid_default, default: 'uuid_generate_v4()'
33
+ t.string :no_default
34
+ end
35
+ execute <<-SQL
36
+ -- not possible via create_table syntax right now
37
+ ALTER TABLE records
38
+ ADD COLUMN function_default float DEFAULT random();
39
+ SQL
40
+ stub_const('Record', Class.new(::ActiveRecord::Base))
41
+
42
+ expect(Record.columns_hash['function_default'].default).to be_nil
43
+ expect(Record.columns_hash['uuid_default'].default).to be_nil
44
+ expect(Record.columns_hash['function_default'].default_function).to_not be_nil
45
+ expect(Record.columns_hash['uuid_default'].default_function).to_not be_nil
46
+
47
+ expect(column_reflector.column_names).
48
+ to eq %i(id uuid_default no_default function_default)
49
+
50
+ expect(column_reflector.without_present_default.column_names).
51
+ to eq %i(no_default)
52
+ end
53
+
54
+ specify 'logs if it encounters an error trying to run default function' do
55
+ create_table :records
56
+ execute <<-SQL
57
+ CREATE FUNCTION raise_error() RETURNS varchar
58
+ AS $BODY$
59
+ BEGIN
60
+ RAISE 'test exception';
61
+ END;
62
+ $BODY$
63
+ LANGUAGE plpgsql;
64
+
65
+ -- not possible via create_table syntax right now
66
+ ALTER TABLE records
67
+ ADD COLUMN function_default varchar DEFAULT raise_error();
68
+ SQL
69
+ stub_const('Record', Class.new(::ActiveRecord::Base))
70
+
71
+ expect(Record.columns_hash['function_default'].default).to be_nil
72
+ expect(Record.columns_hash['function_default'].default_function).to_not be_nil
73
+
74
+ expect(column_reflector.column_names).
75
+ to eq %i(id function_default)
76
+
77
+ expect(SchemaExpectations.error_logger).to receive(:error).once do |message|
78
+ expect(message).to include 'SchemaExpectations: encountered error running SELECT raise_error()'
79
+ expect(message).to include 'PG::RaiseException: ERROR: test exception'
80
+ expect(message).to include ': SELECT raise_error()'
81
+ end
82
+ expect(column_reflector.without_present_default.column_names).
83
+ to eq %i(function_default)
84
+ end
85
+ end
86
+ end
87
+
88
+ specify '#not_null' do
89
+ create_table :records do |t|
90
+ t.string :not_null, null: false
91
+ t.string :nullable
92
+ end
93
+ stub_const('Record', Class.new(::ActiveRecord::Base))
94
+
95
+ expect(column_reflector.column_names).to eq %i(id not_null nullable)
96
+ expect(column_reflector.not_null.column_names).to eq %i(id not_null)
97
+ end
98
+
99
+ context '#for_attributes' do
100
+ before do
101
+ create_table :records do |t|
102
+ t.string :record_id
103
+ t.string :record_type
104
+ t.string :other
105
+ end
106
+
107
+ stub_const('Record', Class.new(::ActiveRecord::Base))
108
+ end
109
+
110
+ specify 'without associations' do
111
+ expect(column_reflector.column_names).to eq %i(id record_id record_type other)
112
+ expect(column_reflector.for_attributes(:missing).column_names).to be_empty
113
+ expect(column_reflector.for_attributes(:record).column_names).to be_empty
114
+ expect(column_reflector.for_attributes(:record_id).column_names).to eq %i(record_id)
115
+ expect(column_reflector.for_attributes(:record_type).column_names).to eq %i(record_type)
116
+ expect(column_reflector.for_attributes(:other).column_names).to eq %i(other)
117
+ expect(column_reflector.for_attributes(*%i(record missing other)).column_names).to eq %i(other)
118
+ end
119
+
120
+ specify 'belongs_to polymorphic' do
121
+ Record.instance_eval do
122
+ belongs_to :record, polymorphic: true
123
+ end
124
+
125
+ expect(column_reflector.column_names).to eq %i(id record_id record_type other)
126
+ expect(column_reflector.for_attributes(:missing).column_names).to be_empty
127
+ expect(column_reflector.for_attributes(:record).column_names).to eq %i(record_id record_type)
128
+ expect(column_reflector.for_attributes(:record_id).column_names).to eq %i(record_id)
129
+ expect(column_reflector.for_attributes(:record_type).column_names).to eq %i(record_type)
130
+ expect(column_reflector.for_attributes(:other).column_names).to eq %i(other)
131
+ expect(column_reflector.for_attributes(*%i(record missing other)).column_names).to eq %i(record_id record_type other)
132
+ end
133
+
134
+ specify 'belongs_to' do
135
+ Record.instance_eval do
136
+ belongs_to :record
137
+ end
138
+
139
+ expect(column_reflector.column_names).to eq %i(id record_id record_type other)
140
+ expect(column_reflector.for_attributes(:missing).column_names).to be_empty
141
+ expect(column_reflector.for_attributes(:record).column_names).to eq %i(record_id)
142
+ expect(column_reflector.for_attributes(:record_id).column_names).to eq %i(record_id)
143
+ expect(column_reflector.for_attributes(:record_type).column_names).to eq %i(record_type)
144
+ expect(column_reflector.for_attributes(:other).column_names).to eq %i(other)
145
+ expect(column_reflector.for_attributes(*%i(record missing other)).column_names).to eq %i(record_id other)
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+ require 'schema_expectations/active_record/validation_reflector'
3
+
4
+ module SchemaExpectations
5
+ module ActiveRecord
6
+ describe ValidationReflector, :active_record do
7
+ before do
8
+ create_table :records
9
+
10
+ stub_const('Record', Class.new(::ActiveRecord::Base))
11
+ end
12
+
13
+ subject(:validation_reflector) { ValidationReflector.new(Record) }
14
+
15
+ specify 'allows filtering attributes' do
16
+ Record.instance_eval do
17
+ validates :present, presence: true
18
+ validates :not_present, length: { minimum: 1 }
19
+ validates :conditional_1, presence: true, on: :create
20
+ validates :conditional_2, presence: true, if: ->{ false }
21
+ validates :conditional_3, presence: true, unless: ->{ false }
22
+ validates :conditional_4, presence: true, allow_nil: true
23
+ validates :conditional_5, presence: true, allow_blank: true
24
+ end
25
+
26
+ expect(validation_reflector.attributes).to eq %i(
27
+ present not_present conditional_1 conditional_2
28
+ conditional_3 conditional_4 conditional_5)
29
+
30
+ expect(validation_reflector.unconditional.attributes).to eq %i(present not_present)
31
+
32
+ expect(validation_reflector.presence.attributes).to eq %i(
33
+ present conditional_1 conditional_2
34
+ conditional_3 conditional_4 conditional_5)
35
+
36
+ expect(validation_reflector.unconditional.presence.attributes).to eq %i(present)
37
+ expect(validation_reflector.presence.unconditional.attributes).to eq %i(present)
38
+ end
39
+
40
+ specify '#conditions_for_attribute' do
41
+ Record.instance_eval do
42
+ validates :present, presence: true
43
+ validates :not_present, length: { minimum: 1 }
44
+ validates :conditional_1, presence: true, on: :create
45
+ validates :conditional_2, presence: true, if: ->{ false }
46
+ validates :conditional_3, presence: true, unless: ->{ false }
47
+ validates :conditional_4, presence: true, allow_nil: true
48
+ validates :conditional_5, presence: true, allow_blank: true
49
+ end
50
+
51
+ expect(validation_reflector.conditions_for_attribute(:missing)).to be_nil
52
+ expect(validation_reflector.conditions_for_attribute(:present)).to be_nil
53
+ expect(validation_reflector.conditions_for_attribute(:not_present)).to be_nil
54
+ expect(validation_reflector.conditions_for_attribute(:conditional_1)).to eq(on: :create)
55
+ expect(validation_reflector.conditions_for_attribute(:conditional_2).keys).to eq [:if]
56
+ expect(validation_reflector.conditions_for_attribute(:conditional_3).keys).to eq [:unless]
57
+ expect(validation_reflector.conditions_for_attribute(:conditional_4)).to eq(allow_nil: true)
58
+ expect(validation_reflector.conditions_for_attribute(:conditional_5)).to eq(allow_blank: true)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+ require 'schema_expectations/config'
3
+
4
+ module SchemaExpectations
5
+ describe Config do
6
+ after do
7
+ SchemaExpectations.configure do |config|
8
+ config.reset!
9
+ end
10
+ end
11
+
12
+ specify 'error_logger' do
13
+ expect(SchemaExpectations.error_logger).to be_a Logger
14
+
15
+ new_logger = Logger.new(StringIO.new)
16
+ SchemaExpectations.configure do |config|
17
+ config.error_logger = new_logger
18
+ end
19
+ expect(SchemaExpectations.error_logger).to be new_logger
20
+ end
21
+ end
22
+ end
@@ -1,106 +1,289 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe :validate_schema_nullable do
4
- specify 'works on instances', :active_record do
5
- create_table :records do |t|
6
- t.string :name, null: false
7
- t.string :wrong
3
+ describe SchemaExpectations::RSpecMatchers::ValidateSchemaNullableMatcher, :active_record do
4
+ shared_examples_for 'Record' do
5
+ def validates(*args)
6
+ Record.instance_eval do
7
+ validates *args
8
+ end
8
9
  end
9
10
 
10
- stub_const('Record', Class.new(ActiveRecord::Base))
11
+ let(:not_null_columns) { [:not_null] }
12
+ let(:nullable_columns) { [:nullable] }
13
+ let(:columns) { not_null_columns + nullable_columns }
11
14
 
12
- Record.instance_eval do
13
- validates :name, :wrong, presence: true
15
+ before do
16
+ create_table :records do |t|
17
+ not_null_columns.each do |column|
18
+ t.string column, null: false
19
+ end
20
+
21
+ nullable_columns.each do |column|
22
+ t.string column
23
+ end
24
+ end
25
+
26
+ stub_const('Record', Class.new(ActiveRecord::Base))
14
27
  end
15
28
 
16
- expect(Record.new).to validate_schema_nullable.only(:name)
17
- expect(Record.new).to_not validate_schema_nullable.only(:wrong)
18
- end
29
+ subject(:record) { Record }
30
+
31
+ context 'with no validations' do
32
+ it { is_expected.to_not validate_schema_nullable }
33
+
34
+ specify 'error messages' do
35
+ expect do
36
+ is_expected.to validate_schema_nullable
37
+ end.to raise_error do |error|
38
+ expect(error).to be_a RSpec::Expectations::ExpectationNotMetError
39
+ not_null_columns.sort.zip(error.message.split(', ')) do |column, message|
40
+ expect(message).to eq "#{column} is NOT NULL but has no presence validation"
41
+ end
42
+ end
43
+ end
44
+
45
+ specify '#only' do
46
+ is_expected.to_not validate_schema_nullable.only(*columns)
47
+ is_expected.to_not validate_schema_nullable.only(*not_null_columns)
48
+ is_expected.to validate_schema_nullable.only(*nullable_columns)
49
+ end
19
50
 
20
- specify 'doesnt raise extraneous exceptions from timestamps', :active_record do
21
- create_table :records do |t|
22
- t.timestamps
51
+ specify '#except' do
52
+ is_expected.to validate_schema_nullable.except(*columns)
53
+ is_expected.to validate_schema_nullable.except(*not_null_columns)
54
+ is_expected.to_not validate_schema_nullable.except(*nullable_columns)
55
+ end
23
56
  end
24
57
 
25
- stub_const('Record', Class.new(ActiveRecord::Base))
58
+ context 'with not_null present' do
59
+ before { validates :not_null, presence: true }
26
60
 
27
- expect(Record.new).to validate_schema_nullable
28
- end
61
+ it { is_expected.to validate_schema_nullable }
62
+
63
+ specify '#only' do
64
+ is_expected.to validate_schema_nullable.only(*columns)
65
+ is_expected.to validate_schema_nullable.only(*not_null_columns)
66
+ is_expected.to validate_schema_nullable.only(*nullable_columns)
67
+ end
68
+
69
+ specify '#except' do
70
+ is_expected.to validate_schema_nullable.except(*columns)
71
+ is_expected.to validate_schema_nullable.except(*not_null_columns)
72
+ is_expected.to validate_schema_nullable.except(*nullable_columns)
73
+ end
74
+ end
75
+
76
+ context 'with nullable present' do
77
+ before { validates :nullable, presence: true }
78
+
79
+ it { is_expected.to_not validate_schema_nullable }
80
+
81
+ specify 'error messages' do
82
+ expect do
83
+ is_expected.to validate_schema_nullable
84
+ end.to raise_error do |error|
85
+ expect(error).to be_a RSpec::Expectations::ExpectationNotMetError
86
+ errors = error.message.split(', ')
87
+
88
+ nullable_columns.sort.zip(errors.take(nullable_columns.size)) do |column, message|
89
+ expect(message).to eq "#{column} has unconditional presence validation but is missing NOT NULL"
90
+ end
91
+
92
+ not_null_columns.sort.zip(errors.drop(nullable_columns.size)) do |column, message|
93
+ expect(message).to eq "#{column} is NOT NULL but has no presence validation"
94
+ end
95
+ end
96
+ end
97
+
98
+ specify '#only' do
99
+ is_expected.to_not validate_schema_nullable.only(*columns)
100
+ is_expected.to_not validate_schema_nullable.only(*not_null_columns)
101
+ is_expected.to_not validate_schema_nullable.only(*nullable_columns)
102
+ end
103
+
104
+ specify '#except' do
105
+ is_expected.to validate_schema_nullable.except(*columns)
106
+ is_expected.to_not validate_schema_nullable.except(*not_null_columns)
107
+ is_expected.to_not validate_schema_nullable.except(*nullable_columns)
108
+ end
109
+ end
110
+
111
+ context 'with nullable and not_null present' do
112
+ before do
113
+ validates :not_null, presence: true
114
+ validates :nullable, presence: true
115
+ end
116
+
117
+ it { is_expected.to_not validate_schema_nullable }
118
+
119
+ specify 'error messages' do
120
+ expect do
121
+ is_expected.to validate_schema_nullable
122
+ end.to raise_error do |error|
123
+ expect(error).to be_a RSpec::Expectations::ExpectationNotMetError
124
+ nullable_columns.sort.zip(error.message.split(', ')) do |column, message|
125
+ expect(message).to eq "#{column} has unconditional presence validation but is missing NOT NULL"
126
+ end
127
+ end
128
+ end
129
+
130
+ specify '#only' do
131
+ is_expected.to_not validate_schema_nullable.only(*columns)
132
+ is_expected.to validate_schema_nullable.only(*not_null_columns)
133
+ is_expected.to_not validate_schema_nullable.only(*nullable_columns)
134
+ end
135
+
136
+ specify '#except' do
137
+ is_expected.to validate_schema_nullable.except(*columns)
138
+ is_expected.to_not validate_schema_nullable.except(*not_null_columns)
139
+ is_expected.to validate_schema_nullable.except(*nullable_columns)
140
+ end
141
+ end
142
+
143
+ specify '#failure_message_when_negated' do
144
+ validates :not_null, presence: true
145
+
146
+ expect do
147
+ is_expected.to_not validate_schema_nullable
148
+ end.to raise_error 'should not match NOT NULL with its presence validation but does'
149
+ end
150
+
151
+ specify 'when primary_key is not id' do
152
+ create_table :records, force: true, id: false do |t|
153
+ t.integer :pk
154
+ end
155
+ Record.reset_column_information
156
+ Record.instance_eval do
157
+ self.primary_key = 'pk'
29
158
 
30
- specify 'asserts that presence validations match NOT NULL', :active_record do
31
- create_table :records do |t|
32
- t.string :not_null, null: false
33
- t.string :not_null_present, null: false
159
+ validates :pk, presence: true
160
+ end
34
161
 
35
- t.string :nullable
36
- t.string :nullable_present
162
+ is_expected.to validate_schema_nullable
37
163
  end
38
164
 
39
- stub_const('Record', Class.new(ActiveRecord::Base))
165
+ specify 'doesnt raise extraneous exceptions from timestamps' do
166
+ create_table :records, force: true do |t|
167
+ t.timestamps null: false
168
+ end
169
+ Record.reset_column_information
40
170
 
41
- Record.instance_eval do
42
- validates :not_null_present, presence: true
43
- validates :nullable_present, presence: true
171
+ is_expected.to validate_schema_nullable
44
172
  end
45
173
 
46
- expect(Record).to validate_schema_nullable.only(:not_null_present, :nullable)
47
- expect(Record).to validate_schema_nullable.except(:not_null, :nullable_present)
174
+ context 'ignores validators with' do
175
+ specify 'on: create' do
176
+ validates :not_null, presence: true, on: :create
177
+
178
+ expect do
179
+ is_expected.to validate_schema_nullable.only(*not_null_columns)
180
+ end.to raise_error do |error|
181
+ expect(error).to be_a RSpec::Expectations::ExpectationNotMetError
182
+ not_null_columns.sort.zip(error.message.split(', ')) do |column, message|
183
+ expect(message).to eq "#{column} is NOT NULL but its presence validator was conditional: {:on=>:create}"
184
+ end
185
+ end
186
+ end
48
187
 
49
- expect(Record).to_not validate_schema_nullable
50
- expect(Record).to_not validate_schema_nullable.only(:not_null)
51
- expect(Record).to_not validate_schema_nullable.only(:nullable_present)
188
+ specify 'if: proc' do
189
+ validates :not_null, presence: true, if: ->{ false }
52
190
 
53
- expect do
54
- expect(Record).to validate_schema_nullable.only(:not_null)
55
- end.to raise_error 'not_null is NOT NULL but has no presence validation'
191
+ expect do
192
+ is_expected.to validate_schema_nullable.only(*not_null_columns)
193
+ end.to raise_error do |error|
194
+ expect(error).to be_a RSpec::Expectations::ExpectationNotMetError
195
+ not_null_columns.sort.zip(error.message.split(', ')) do |column, message|
196
+ expect(message).to match /\A#{column} is NOT NULL but its presence validator was conditional: {:if=>\#<Proc:.*>}\z/
197
+ end
198
+ end
199
+ end
56
200
 
57
- expect do
58
- expect(Record).to validate_schema_nullable.only(:nullable_present)
59
- end.to raise_error 'nullable_present has unconditional presence validation but is missing NOT NULL'
201
+ specify 'unless: proc' do
202
+ validates :not_null, presence: true, unless: ->{ true }
60
203
 
61
- Record.instance_eval do
62
- clear_validators!
63
- validates :not_null_present, presence: true, on: :create
204
+ expect do
205
+ is_expected.to validate_schema_nullable.only(*not_null_columns)
206
+ end.to raise_error do |error|
207
+ expect(error).to be_a RSpec::Expectations::ExpectationNotMetError
208
+ not_null_columns.sort.zip(error.message.split(', ')) do |column, message|
209
+ expect(message).to match /\A#{column} is NOT NULL but its presence validator was conditional: {:unless=>\#<Proc:.*>}\z/
210
+ end
211
+ end
212
+ end
213
+
214
+ specify 'allow_nil: true' do
215
+ validates :not_null, presence: true, allow_nil: true
216
+
217
+ expect do
218
+ is_expected.to validate_schema_nullable.only(*not_null_columns)
219
+ end.to raise_error do |error|
220
+ expect(error).to be_a RSpec::Expectations::ExpectationNotMetError
221
+ not_null_columns.sort.zip(error.message.split(', ')) do |column, message|
222
+ expect(message).to eq "#{column} is NOT NULL but its presence validator was conditional: {:allow_nil=>true}"
223
+ end
224
+ end
225
+ end
226
+
227
+ specify 'allow_blank: true' do
228
+ validates :not_null, presence: true, allow_blank: true
229
+
230
+ expect do
231
+ is_expected.to validate_schema_nullable.only(*not_null_columns)
232
+ end.to raise_error do |error|
233
+ expect(error).to be_a RSpec::Expectations::ExpectationNotMetError
234
+ not_null_columns.sort.zip(error.message.split(', ')) do |column, message|
235
+ expect(message).to eq "#{column} is NOT NULL but its presence validator was conditional: {:allow_blank=>true}"
236
+ end
237
+ end
238
+ end
64
239
  end
65
- expect(Record).to_not validate_schema_nullable.only(:not_null_present)
66
- expect do
67
- expect(Record).to validate_schema_nullable.only(:not_null_present)
68
- end.to raise_error 'not_null_present is NOT NULL but its presence validator was conditional: {:on=>:create}'
69
-
70
- Record.instance_eval do
71
- clear_validators!
72
- validates :not_null_present, presence: true, if: ->{ false }
240
+ end
241
+
242
+ context 'called on class' do
243
+ include_examples 'Record' do
244
+ subject(:record) { Record }
73
245
  end
74
- expect(Record).to_not validate_schema_nullable.only(:not_null_present)
75
- expect do
76
- expect(Record).to validate_schema_nullable.only(:not_null_present)
77
- end.to raise_error /\Anot_null_present is NOT NULL but its presence validator was conditional: {:if=>\#<Proc:.*>}\z/
78
-
79
- Record.instance_eval do
80
- clear_validators!
81
- validates :not_null_present, presence: true, unless: ->{ true }
246
+ end
247
+
248
+ context 'called on instance' do
249
+ include_examples 'Record' do
250
+ subject(:record) { Record.new }
82
251
  end
83
- expect(Record).to_not validate_schema_nullable.only(:not_null_present)
84
- expect do
85
- expect(Record).to validate_schema_nullable.only(:not_null_present)
86
- end.to raise_error /\Anot_null_present is NOT NULL but its presence validator was conditional: {:unless=>\#<Proc:.*>}\z/
87
-
88
- Record.instance_eval do
89
- clear_validators!
90
- validates :not_null_present, presence: true, allow_nil: true
252
+ end
253
+
254
+ specify 'called on unrecognized object' do
255
+ expect { expect(double('object')).to validate_schema_nullable }.
256
+ to raise_error /#<RSpec::Mocks::Double:0x\h* @name="object"> does not inherit from ActiveRecord::Base/
257
+ end
258
+
259
+ context 'with belongs_to associations' do
260
+ include_examples 'Record' do
261
+ let(:not_null_columns) { [:not_null_id] }
262
+ let(:nullable_columns) { [:nullable_id] }
263
+
264
+ before do
265
+ create_table :other_records
266
+ stub_const('OtherRecord', Class.new(ActiveRecord::Base))
267
+
268
+ Record.instance_eval do
269
+ belongs_to :not_null, class_name: 'OtherRecord', foreign_key: :not_null_id
270
+ belongs_to :nullable, class_name: 'OtherRecord', foreign_key: :nullable_id
271
+ end
272
+ end
91
273
  end
92
- expect(Record).to_not validate_schema_nullable.only(:not_null_present)
93
- expect do
94
- expect(Record).to validate_schema_nullable.only(:not_null_present)
95
- end.to raise_error 'not_null_present is NOT NULL but its presence validator was conditional: {:allow_nil=>true}'
96
-
97
- Record.instance_eval do
98
- clear_validators!
99
- validates :not_null_present, presence: true, allow_blank: true
274
+ end
275
+
276
+ context 'with polymorphic belongs_to associations' do
277
+ include_examples 'Record' do
278
+ let(:not_null_columns) { [:not_null_id, :not_null_type] }
279
+ let(:nullable_columns) { [:nullable_id, :nullable_type] }
280
+
281
+ before do
282
+ Record.instance_eval do
283
+ belongs_to :not_null, polymorphic: true, foreign_key: :not_null_id, foreign_type: :not_null_type
284
+ belongs_to :nullable, polymorphic: true, foreign_key: :nullable_id, foreign_type: :nullable_type
285
+ end
286
+ end
100
287
  end
101
- expect(Record).to_not validate_schema_nullable.only(:not_null_present)
102
- expect do
103
- expect(Record).to validate_schema_nullable.only(:not_null_present)
104
- end.to raise_error 'not_null_present is NOT NULL but its presence validator was conditional: {:allow_blank=>true}'
105
288
  end
106
289
  end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ require 'schema_expectations/util'
3
+
4
+ module SchemaExpectations
5
+ describe SchemaExpectations::Util do
6
+ specify '.slice_hash' do
7
+ expect(Util.slice_hash({}, :key)).to eq({})
8
+ expect(Util.slice_hash({ key: :value }, :key)).to eq(key: :value)
9
+ expect(Util.slice_hash({ other: :value }, :key)).to eq({})
10
+ expect(Util.slice_hash({
11
+ key_1: :value_1,
12
+ key_2: :value_2,
13
+ other: :value_3 }, :key_1, :key_2)).to eq(key_1: :value_1, key_2: :value_2)
14
+ end
15
+ end
16
+ end
data/spec/meta_spec.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe SchemaExpectations do
4
+ specify 'Appraisals and .travis.yml are synced' do
5
+ env_bundle_gemfile = ENV['BUNDLE_GEMFILE']
6
+ ENV.delete 'BUNDLE_GEMFILE'
7
+
8
+ require 'appraisal'
9
+ require 'yaml'
10
+
11
+ travis_file = File.expand_path('../../.travis.yml', __FILE__)
12
+ travis_config = YAML.load(File.read(travis_file))
13
+
14
+ Appraisal::File.each do |appraisal|
15
+ error_message = "Appraisal #{appraisal.name} is out of sync. Run `rake refresh`"
16
+ expect(File.file?(appraisal.gemfile_path)).to be_truthy, error_message
17
+ expect(File.read(appraisal.gemfile_path)).to include(appraisal.gemfile.to_s), error_message
18
+ expect(travis_config['gemfile']).to be_a(Array), error_message
19
+ expect(travis_config['gemfile']).to include("gemfiles/#{File.basename(appraisal.gemfile_path)}"), error_message
20
+ end
21
+
22
+ ENV['BUNDLE_GEMFILE'] = env_bundle_gemfile
23
+ end
24
+
25
+ specify 'binding.pry is not committed' do
26
+ bindings = `git grep binding.pry`.split("\n")
27
+ bindings.reject! do |binding|
28
+ binding.start_with? 'spec/meta_spec.rb:'
29
+ end
30
+ expect(bindings).to be_empty
31
+ end
32
+
33
+ specify 'tests connect to postgresql', :active_record, :postgresql do
34
+ expect(ActiveRecord::Base.connection_config[:adapter]).to eq 'postgresql'
35
+ end
36
+
37
+ specify 'tests connect to sqlite3', :active_record, :sqlite3 do
38
+ expect(ActiveRecord::Base.connection_config[:adapter]).to eq 'sqlite3'
39
+ end
40
+
41
+ specify 'tests connect to mysql2', :active_record, :mysql2 do
42
+ expect(ActiveRecord::Base.connection_config[:adapter]).to eq 'mysql2'
43
+ end
44
+ end