polymorpheus 3.1.0 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,167 +1,317 @@
1
- require 'active_record'
2
1
  require 'spec_helper'
3
- require 'sql_logger'
4
- require 'polymorpheus'
5
- require 'polymorpheus/trigger'
6
- require 'shared_examples'
7
-
8
- Polymorpheus::Adapter.load!
9
2
 
10
3
  describe Polymorpheus::ConnectionAdapters::MysqlAdapter do
4
+ # The foreign key name is not truncated, so the maximum column name
5
+ # length ends up being: 64 - "pets_" - "_fk" == 56
6
+ #
7
+ # Using `t.references` also adds an index on this column, with a slightly
8
+ # longer name: 64 - "index_pets_on_" - "_id" == 46
9
+ #
10
+ # Go with the shorter of the two here, since it's still long enough to test
11
+ # the generation of Polymorpheus' trigger names.
12
+ let(:long_column1) { ('x' * 46).to_sym }
13
+ let(:long_column2) { ('y' * 46).to_sym }
11
14
 
12
- #######################################################
13
- # Setup
14
- #######################################################
15
-
16
- before(:all) do
17
- class << ActiveRecord::Base.connection
18
- include Polymorpheus::SqlLogger
19
- alias_method :original_execute, :execute
20
- alias_method :execute, :log_sql_statements
15
+ before do
16
+ create_table(:pets) do |t|
17
+ t.references :cat
18
+ t.references :dog
19
+ t.string :name
20
+ t.string :color
21
21
  end
22
- end
23
22
 
24
- after(:all) do
25
- class << ActiveRecord::Base.connection
26
- alias_method :execute, :original_execute
27
- end
28
- end
23
+ create_table(:cats)
24
+ create_table(:dogs)
29
25
 
30
- let(:connection) { ActiveRecord::Base.connection }
31
- let(:sql) { connection.sql_statements }
32
-
33
- def clean_sql(sql_string)
34
- sql_string.gsub(/^\n\s*/,'').gsub(/\s*\n\s*$/,'')
35
- .gsub(/\n\s*/,"\n").gsub(/\s*$/,"")
36
- .gsub('`', '')
37
- .gsub(/\ FOREIGN KEY/, "\nFOREIGN KEY")
38
- .gsub(/\ REFERENCES/, "\nREFERENCES")
39
- .gsub(/\ ON DELETE/, "\nON DELETE")
40
- .gsub(/\ ON UPDATE/, "\nON UPDATE")
41
- .gsub(/([[:alpha:]])\(/, '\1 (')
26
+ clear_sql_history
42
27
  end
43
28
 
44
- before do
45
- connection.clear_sql_history
46
- subject
29
+ after do
30
+ drop_table :pets
31
+ drop_table :cats
32
+ drop_table :dogs
47
33
  end
48
34
 
49
35
  #######################################################
50
36
  # Specs
51
37
  #######################################################
52
38
 
53
- describe "migration statements" do
54
- context "basic case with no uniqueness constraints" do
55
- include_context "columns with short names"
56
- let(:options) { {} }
39
+ describe '#add_polymorphic_constraints' do
40
+ it 'adds foreign keys with no uniqueness constraints' do
41
+ add_polymorphic_constraints(
42
+ 'pets',
43
+ { cat_id: 'cats.id', dog_id: 'dogs.id' }
44
+ )
45
+
46
+ should_execute_sql <<-EOS
47
+ DROP TRIGGER IF EXISTS pfki_pets_catid_dogid
48
+ DROP TRIGGER IF EXISTS pfku_pets_catid_dogid
49
+ CREATE TRIGGER pfki_pets_catid_dogid BEFORE INSERT ON pets
50
+ FOR EACH ROW
51
+ BEGIN
52
+ IF(IF(NEW.cat_id IS NULL, 0, 1) + IF(NEW.dog_id IS NULL, 0, 1)) <> 1 THEN
53
+ SET NEW = 'Error';
54
+ END IF;
55
+ END
56
+ CREATE TRIGGER pfku_pets_catid_dogid BEFORE UPDATE ON pets
57
+ FOR EACH ROW
58
+ BEGIN
59
+ IF(IF(NEW.cat_id IS NULL, 0, 1) + IF(NEW.dog_id IS NULL, 0, 1)) <> 1 THEN
60
+ SET NEW = 'Error';
61
+ END IF;
62
+ END
63
+
64
+ ALTER TABLE `pets` ADD CONSTRAINT `pets_cat_id_fk`
65
+ FOREIGN KEY (`cat_id`)
66
+ REFERENCES `cats`(id)
57
67
 
58
- it_behaves_like "mysql2 migration statements"
68
+ ALTER TABLE `pets` ADD CONSTRAINT `pets_dog_id_fk`
69
+ FOREIGN KEY (`dog_id`)
70
+ REFERENCES `dogs`(id)
71
+ EOS
59
72
  end
60
73
 
61
- context "when uniqueness constraint is specified as true" do
62
- include_context "columns with short names"
63
- let(:options) { { :unique => true } }
64
- let(:unique_key_sql) do
65
- %{ CREATE UNIQUE INDEX pfk_pets_dogid ON pets (dog_id)
66
- CREATE UNIQUE INDEX pfk_pets_kittyid ON pets (kitty_id) }
67
- end
68
- let(:remove_indices_sql) do
69
- %{ DROP INDEX pfk_pets_kittyid ON pets
70
- DROP INDEX pfk_pets_dogid ON pets }
71
- end
74
+ it 'adds uniqueness specified with true' do
75
+ add_polymorphic_constraints(
76
+ 'pets',
77
+ { cat_id: 'cats.id', dog_id: 'dogs.id' },
78
+ unique: true
79
+ )
72
80
 
73
- it_behaves_like "mysql2 migration statements"
81
+ should_execute_sql <<-EOS
82
+ CREATE UNIQUE INDEX pfk_pets_catid ON pets (cat_id)
83
+ CREATE UNIQUE INDEX pfk_pets_dogid ON pets (dog_id)
84
+ EOS
74
85
  end
75
86
 
76
- context "specifying uniqueness constraint as a string" do
77
- include_context "columns with short names"
78
- let(:options) { { :unique => 'field1' } }
79
- let(:unique_key_sql) do
80
- %{ CREATE UNIQUE INDEX pfk_pets_dogid_field1 ON pets (dog_id, field1)
81
- CREATE UNIQUE INDEX pfk_pets_kittyid_field1 ON pets (kitty_id, field1) }
82
- end
83
- let(:remove_indices_sql) do
84
- %{ DROP INDEX pfk_pets_kittyid_field1 ON pets
85
- DROP INDEX pfk_pets_dogid_field1 ON pets }
86
- end
87
+ it 'adds uniqueness specified with a string' do
88
+ add_polymorphic_constraints('pets', { cat_id: 'cats.id' }, unique: 'name')
89
+ should_execute_sql <<-EOS
90
+ CREATE UNIQUE INDEX pfk_pets_catid_name ON pets (cat_id, name)
91
+ EOS
92
+ end
87
93
 
88
- it_behaves_like "mysql2 migration statements"
94
+ it 'adds uniqueness specified as an array' do
95
+ add_polymorphic_constraints(
96
+ 'pets',
97
+ { cat_id: 'cats.id' },
98
+ unique: [:name, :color]
99
+ )
100
+ should_execute_sql <<-EOS
101
+ CREATE UNIQUE INDEX pfk_pets_catid_name_color ON pets (cat_id, name, color)
102
+ EOS
89
103
  end
90
104
 
91
- context "specifying uniqueness constraint as an array" do
92
- include_context "columns with short names"
93
- let(:options) { { :unique => [:foo, :bar] } }
94
- let(:unique_key_sql) do
95
- %{ CREATE UNIQUE INDEX pfk_pets_dogid_foo_bar ON pets (dog_id, foo, bar)
96
- CREATE UNIQUE INDEX pfk_pets_kittyid_foo_bar ON pets (kitty_id, foo, bar) }
97
- end
98
- let(:remove_indices_sql) do
99
- %{ DROP INDEX pfk_pets_kittyid_foo_bar ON pets
100
- DROP INDEX pfk_pets_dogid_foo_bar ON pets }
101
- end
105
+ it 'adds an on update constraint' do
106
+ add_polymorphic_constraints(
107
+ 'pets',
108
+ { cat_id: 'cats.id' },
109
+ on_update: :cascade
110
+ )
111
+ should_execute_sql <<-EOS
112
+ ALTER TABLE `pets` ADD CONSTRAINT `pets_cat_id_fk`
113
+ FOREIGN KEY (`cat_id`)
114
+ REFERENCES `cats`(id)
115
+ ON UPDATE CASCADE
116
+ EOS
117
+ end
102
118
 
103
- it_behaves_like "mysql2 migration statements"
119
+ it 'raises an error when on_update has invalid arguments' do
120
+ expect do
121
+ add_polymorphic_constraints(
122
+ 'pets',
123
+ { cat_id: 'cats.id' },
124
+ on_update: :invalid
125
+ )
126
+ end.to raise_error ArgumentError
104
127
  end
105
128
 
106
- context "specifying an on update constraint" do
107
- include_context "columns with short names"
108
- let(:options) { { :on_update => :cascade } }
109
- let(:fkey_sql) do
110
- %{ ALTER TABLE `pets` ADD CONSTRAINT `pets_dog_id_fk` FOREIGN KEY (`dog_id`) REFERENCES `dogs`(id) ON UPDATE CASCADE
111
- ALTER TABLE `pets` ADD CONSTRAINT `pets_kitty_id_fk` FOREIGN KEY (`kitty_id`) REFERENCES `cats`(name) ON UPDATE CASCADE }
112
- end
129
+ it 'adds an on delete constraint' do
130
+ add_polymorphic_constraints(
131
+ 'pets',
132
+ { cat_id: 'cats.id' },
133
+ on_delete: :cascade
134
+ )
135
+ should_execute_sql <<-EOS
136
+ ALTER TABLE `pets` ADD CONSTRAINT `pets_cat_id_fk`
137
+ FOREIGN KEY (`cat_id`)
138
+ REFERENCES `cats`(id)
139
+ ON DELETE CASCADE
140
+ EOS
141
+ end
113
142
 
114
- it_behaves_like "mysql2 migration statements"
143
+ it 'raises an error when on_delete has invalid arguments' do
144
+ expect do
145
+ add_polymorphic_constraints(
146
+ 'pets',
147
+ { cat_id: 'cats.id' },
148
+ on_update: :invalid
149
+ )
150
+ end.to raise_error ArgumentError
115
151
  end
116
152
 
117
- context "specifying on delete and on update constraints" do
118
- include_context "columns with short names"
119
- let(:options) { { :on_update => :cascade, :on_delete => :restrict } }
120
- let(:fkey_sql) do
121
- %{ ALTER TABLE `pets` ADD CONSTRAINT `pets_dog_id_fk` FOREIGN KEY (`dog_id`) REFERENCES `dogs`(id) ON DELETE RESTRICT ON UPDATE CASCADE
122
- ALTER TABLE `pets` ADD CONSTRAINT `pets_kitty_id_fk` FOREIGN KEY (`kitty_id`) REFERENCES `cats`(name) ON DELETE RESTRICT ON UPDATE CASCADE }
153
+ it 'truncates long trigger names to 64 characters' do
154
+ create_table(:pets) do |t|
155
+ t.references long_column1
156
+ t.references long_column2
123
157
  end
124
158
 
125
- it_behaves_like "mysql2 migration statements"
159
+ add_polymorphic_constraints(
160
+ 'pets',
161
+ { "#{long_column1}_id" => 'cats.id', "#{long_column2}_id" => 'dogs.id' }
162
+ )
163
+
164
+ should_execute_sql <<-EOS
165
+ DROP TRIGGER IF EXISTS pfki_pets_xxxxxxxxxxxxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyyyy
166
+ DROP TRIGGER IF EXISTS pfku_pets_xxxxxxxxxxxxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyyyy
167
+ CREATE TRIGGER pfki_pets_xxxxxxxxxxxxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyyyy BEFORE INSERT ON pets
168
+ FOR EACH ROW
169
+ BEGIN
170
+ IF(IF(NEW.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_id IS NULL, 0, 1) + IF(NEW.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy_id IS NULL, 0, 1)) <> 1 THEN
171
+ SET NEW = 'Error';
172
+ END IF;
173
+ END
174
+ CREATE TRIGGER pfku_pets_xxxxxxxxxxxxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyyyy BEFORE UPDATE ON pets
175
+ FOR EACH ROW
176
+ BEGIN
177
+ IF(IF(NEW.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_id IS NULL, 0, 1) + IF(NEW.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy_id IS NULL, 0, 1)) <> 1 THEN
178
+ SET NEW = 'Error';
179
+ END IF;
180
+ END
181
+ EOS
126
182
  end
183
+ end
127
184
 
128
- context "when on_delete and on_update have invalid arguments" do
129
- include_context "columns with short names"
130
- let(:options) { { :on_update => :invalid, :on_delete => nil } }
131
- let(:fkey_sql) do
132
- %{ ALTER TABLE `pets` ADD CONSTRAINT `pets_dog_id_fk` FOREIGN KEY (`dog_id`) REFERENCES `dogs`(id)
133
- ALTER TABLE `pets` ADD CONSTRAINT `pets_kitty_id_fk` FOREIGN KEY (`kitty_id`) REFERENCES `cats`(name) }
134
- end
185
+ describe '#remove_polymorphic_constraints' do
186
+ it 'removes triggers and foreign keys with no uniqueness constraints' do
187
+ add_polymorphic_constraints(
188
+ 'pets',
189
+ { cat_id: 'cats.id', dog_id: 'dogs.id' },
190
+ unique: true
191
+ )
192
+ clear_sql_history
135
193
 
136
- it "#add_polymorphic_constraints raises an argument error" do
137
- expect do
138
- connection.add_polymorphic_constraints(table, columns, options)
139
- end.to raise_error ArgumentError
140
- end
194
+ remove_polymorphic_constraints(
195
+ 'pets',
196
+ { cat_id: 'cats.id', dog_id: 'dogs.id' }
197
+ )
198
+ should_execute_sql <<-EOS
199
+ DROP TRIGGER IF EXISTS pfki_pets_catid_dogid
200
+ DROP TRIGGER IF EXISTS pfku_pets_catid_dogid
201
+ EOS
202
+ end
203
+
204
+ it 'removes uniqueness index specified with true' do
205
+ add_polymorphic_constraints(
206
+ 'pets',
207
+ { cat_id: 'cats.id', dog_id: 'dogs.id' },
208
+ unique: true
209
+ )
210
+ clear_sql_history
141
211
 
142
- it_behaves_like 'mysql2 add sql for polymorphic triggers'
143
- it_behaves_like 'mysql2 remove sql for polymorphic constraints'
212
+ remove_polymorphic_constraints(
213
+ 'pets',
214
+ { cat_id: 'cats.id', dog_id: 'dogs.id' },
215
+ unique: true
216
+ )
217
+ should_execute_sql <<-EOS
218
+ DROP INDEX pfk_pets_catid ON pets
219
+ DROP INDEX pfk_pets_dogid ON pets
220
+ EOS
144
221
  end
145
222
 
146
- context "when table and column names combined are very long" do
147
- include_context "columns with long names"
223
+ it 'removes uniqueness index specified with a string' do
224
+ add_polymorphic_constraints(
225
+ 'pets',
226
+ { cat_id: 'cats.id', dog_id: 'dogs.id' },
227
+ unique: 'name'
228
+ )
229
+ clear_sql_history
230
+
231
+ remove_polymorphic_constraints(
232
+ 'pets',
233
+ { cat_id: 'cats.id', dog_id: 'dogs.id' },
234
+ unique: 'name'
235
+ )
148
236
 
149
- it_behaves_like "mysql2 migration statements"
150
237
  end
151
- end
152
238
 
153
- describe "#triggers" do
154
- let(:trigger1) { double(Trigger, :name => '1') }
155
- let(:trigger2) { double(Trigger, :name => '2') }
239
+ it 'removes uniqueness index specified with an array' do
240
+ add_polymorphic_constraints(
241
+ 'pets',
242
+ { cat_id: 'cats.id', dog_id: 'dogs.id' },
243
+ unique: [:name, :color]
244
+ )
245
+ clear_sql_history
246
+
247
+ remove_polymorphic_constraints(
248
+ 'pets',
249
+ { cat_id: 'cats.id', dog_id: 'dogs.id' },
250
+ unique: [:name, :color]
251
+ )
252
+
253
+ end
254
+
255
+ it 'removes an on update constraint' do
256
+ add_polymorphic_constraints(
257
+ 'pets',
258
+ { cat_id: 'cats.id', dog_id: 'dogs.id' },
259
+ on_update: :cascade
260
+ )
261
+ clear_sql_history
262
+
263
+ remove_polymorphic_constraints(
264
+ 'pets',
265
+ { cat_id: 'cats.id', dog_id: 'dogs.id' },
266
+ on_update: :cascade
267
+ )
268
+
269
+ end
270
+
271
+ it 'removes an on delete constraint' do
272
+ add_polymorphic_constraints(
273
+ 'pets',
274
+ { cat_id: 'cats.id', dog_id: 'dogs.id' },
275
+ on_delete: :cascade
276
+ )
277
+ clear_sql_history
278
+
279
+ remove_polymorphic_constraints(
280
+ 'pets',
281
+ { cat_id: 'cats.id', dog_id: 'dogs.id' },
282
+ on_update: :cascade
283
+ )
156
284
 
157
- before do
158
- connection.stub_sql('show triggers', [:trigger1, :trigger2])
159
- Trigger.stub(:new).with(:trigger1).and_return(trigger1)
160
- Trigger.stub(:new).with(:trigger2).and_return(trigger2)
161
285
  end
162
286
 
163
- specify do
164
- connection.triggers.should == [trigger1, trigger2]
287
+ it 'truncates long trigger names to 64 characters' do
288
+ create_table(:pets) do |t|
289
+ t.references long_column1
290
+ t.references long_column2
291
+ end
292
+ args = [
293
+ 'pets',
294
+ { "#{long_column1}_id" => 'cats.id', "#{long_column2}_id" => 'dogs.id' }
295
+ ]
296
+ add_polymorphic_constraints(*args)
297
+ clear_sql_history
298
+ remove_polymorphic_constraints(*args)
299
+ should_execute_sql <<-EOS
300
+ DROP TRIGGER IF EXISTS pfki_pets_xxxxxxxxxxxxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyyyy
301
+ DROP TRIGGER IF EXISTS pfku_pets_xxxxxxxxxxxxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyyyy
302
+ EOS
303
+ end
304
+ end
305
+
306
+ describe "#triggers" do
307
+ it 'returns the triggers for the current schema' do
308
+ add_polymorphic_constraints(
309
+ 'pets',
310
+ { cat_id: 'cats.id', dog_id: 'dogs.id' }
311
+ )
312
+ expect(triggers.map(&:name)).to eq(
313
+ ['pfki_pets_catid_dogid', 'pfku_pets_catid_dogid']
314
+ )
165
315
  end
166
316
  end
167
317
  end
@@ -1,38 +1,32 @@
1
- require 'active_record'
2
1
  require 'spec_helper'
3
- require 'sql_logger'
4
- require 'polymorpheus'
5
- require 'polymorpheus/trigger'
6
- require 'stringio'
7
-
8
- # this is normally done via a Railtie in non-testing situations
9
- ActiveRecord::SchemaDumper.class_eval { include Polymorpheus::SchemaDumper }
10
2
 
11
3
  describe Polymorpheus::SchemaDumper do
12
-
13
4
  let(:connection) { ActiveRecord::Base.connection }
14
5
  let(:stream) { StringIO.new }
15
6
 
16
7
  before do
17
- # pretend like we have a trigger defined
18
- connection.stub(:triggers).and_return(
19
- [Trigger.new(["trigger_name", "INSERT", "pets",
20
- %{BEGIN
21
- IF(IF(NEW.dog_id IS NULL, 0, 1) + IF(NEW.kitty_id IS NULL, 0, 1)) <> 1 THEN
22
- SET NEW = 'Error';
23
- END IF;
24
- END},
25
- "BEFORE", nil, "", "production@%", "utf8", "utf8_general_ci",
26
- "utf8_unicode_ci"])]
8
+ create_table :story_arcs do |t|
9
+ t.references :hero
10
+ t.references :villain
11
+ end
12
+ create_table :heros
13
+ create_table :villains
14
+ ActiveRecord::Base.connection.add_polymorphic_constraints(
15
+ 'story_arcs',
16
+ { hero_id: 'heros.id', villain_id: 'villains.id' }
27
17
  )
28
18
 
29
19
  ActiveRecord::SchemaDumper.dump(connection, stream)
30
20
  end
31
21
 
22
+ after do
23
+ drop_table :story_arcs # drop first, due to the foreign key
24
+ end
25
+
32
26
  subject { stream.string }
33
27
 
34
28
  let(:schema_statement) do
35
- %{ add_polymorphic_triggers(:pets, ["dog_id", "kitty_id"])}
29
+ %{ add_polymorphic_triggers(:story_arcs, ["hero_id", "villain_id"])}
36
30
  end
37
31
 
38
32
  specify "the schema statement is part of the dump" do
@@ -42,5 +36,4 @@ describe Polymorpheus::SchemaDumper do
42
36
  specify "there is exactly one instance of the schema statement" do
43
37
  subject.index(schema_statement).should == subject.rindex(schema_statement)
44
38
  end
45
-
46
39
  end