convergence 1.0.6 → 1.2.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +35 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +57 -0
- data/CONTRIBUTING.md +80 -0
- data/Gemfile.lock +25 -9
- data/README.md +160 -1
- data/Rakefile +45 -2
- data/convergence.gemspec +5 -1
- data/lib/convergence/cli.rb +10 -2
- data/lib/convergence/column.rb +3 -2
- data/lib/convergence/command/apply.rb +38 -9
- data/lib/convergence/command/dryrun.rb +5 -5
- data/lib/convergence/command/export.rb +19 -1
- data/lib/convergence/command.rb +6 -0
- data/lib/convergence/database_connector/postgres_connector.rb +25 -0
- data/lib/convergence/database_connector/sqlite_connector.rb +22 -0
- data/lib/convergence/database_connector.rb +6 -0
- data/lib/convergence/default_parameter/mysql_default_parameter.rb +1 -1
- data/lib/convergence/default_parameter/postgres_default_parameter.rb +57 -0
- data/lib/convergence/default_parameter/sqlite_default_parameter.rb +57 -0
- data/lib/convergence/default_parameter.rb +6 -0
- data/lib/convergence/diff.rb +46 -3
- data/lib/convergence/dsl.rb +18 -3
- data/lib/convergence/dumper/mysql_schema_dumper.rb +37 -9
- data/lib/convergence/dumper/postgres_schema_dumper.rb +193 -0
- data/lib/convergence/dumper/sqlite_schema_dumper.rb +132 -0
- data/lib/convergence/dumper.rb +113 -0
- data/lib/convergence/sql_generator/mysql_generator.rb +32 -3
- data/lib/convergence/sql_generator/postgres_generator.rb +270 -0
- data/lib/convergence/sql_generator/sqlite_generator.rb +192 -0
- data/lib/convergence/table.rb +3 -2
- data/lib/convergence/version.rb +1 -1
- data/spec/config/spec_database.yml +12 -0
- data/spec/convergence/config_spec.rb +75 -0
- data/spec/convergence/diff_spec.rb +291 -44
- data/spec/convergence/dsl_spec.rb +26 -0
- data/spec/convergence/dumper/mysql_schema_dumper_spec.rb +25 -2
- data/spec/convergence/dumper/postgres_schema_dumper_spec.rb +103 -0
- data/spec/convergence/dumper/sqlite_schema_dumper_spec.rb +85 -0
- data/spec/convergence/dumper_spec.rb +110 -16
- data/spec/convergence/foreign_key_spec.rb +33 -0
- data/spec/convergence/index_spec.rb +52 -0
- data/spec/convergence/pretty_diff_spec.rb +85 -0
- data/spec/convergence/table_spec.rb +46 -0
- data/spec/fixtures/add_table_with_enum_set.schema +5 -0
- data/spec/fixtures/change_table_comment_to_paper.schema +2 -0
- data/spec/fixtures/execute_raw_sql.schema +28 -0
- data/spec/fixtures/postgres/add_columns_to_paper.schema +29 -0
- data/spec/fixtures/postgres/add_table.schema +32 -0
- data/spec/fixtures/postgres/change_comment_columns_to_paper.schema +28 -0
- data/spec/fixtures/postgres/change_table_comment_to_paper.schema +28 -0
- data/spec/fixtures/postgres/drop_foreign_key.schema +25 -0
- data/spec/fixtures/postgres/drop_table.schema +19 -0
- data/spec/fixtures/postgres/remove_columns_to_paper.schema +27 -0
- data/spec/fixtures/postgres/rename_column_on_paper.schema +28 -0
- data/spec/fixtures/postgres/rename_table.schema +28 -0
- data/spec/fixtures/postgres_test_db.sql +41 -0
- data/spec/fixtures/rename_column_to_author.schema +26 -0
- data/spec/fixtures/rename_table.schema +26 -0
- data/spec/fixtures/sqlite/add_columns_to_paper.schema +29 -0
- data/spec/fixtures/sqlite/add_table.schema +32 -0
- data/spec/fixtures/sqlite/change_columns_to_paper.schema +28 -0
- data/spec/fixtures/sqlite/drop_foreign_key.schema +25 -0
- data/spec/fixtures/sqlite/drop_table.schema +19 -0
- data/spec/fixtures/sqlite/remove_columns_to_paper.schema +27 -0
- data/spec/fixtures/sqlite/rename_column_on_paper.schema +28 -0
- data/spec/fixtures/sqlite/rename_table.schema +27 -0
- data/spec/fixtures/sqlite_test_db.sql +31 -0
- data/spec/fixtures/test_db.sql +10 -0
- data/spec/integrations/command_diff.rb +35 -0
- data/spec/integrations/command_dryrun.rb +59 -2
- data/spec/integrations/command_export.rb +28 -0
- data/spec/postgres_integrations/command_dryrun.rb +101 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/sqlite_integrations/command_dryrun.rb +92 -0
- metadata +135 -7
|
@@ -64,6 +64,96 @@ describe Convergence::DSL do
|
|
|
64
64
|
end
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
+
context 'when a table is renamed' do
|
|
68
|
+
let(:renamed_table) do
|
|
69
|
+
Convergence::Table.new('simple_table_renamed', renamed_from: 'simple_table').tap do |t|
|
|
70
|
+
t.int('id', primary_key: true)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
let(:from_db) do
|
|
74
|
+
{ 'simple_table' => simple_table }
|
|
75
|
+
end
|
|
76
|
+
let(:to_db) do
|
|
77
|
+
{ 'simple_table_renamed' => renamed_table }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
subject { Convergence::Diff.new.diff(from_db, to_db) }
|
|
81
|
+
|
|
82
|
+
it 'should detect it as a rename, not add/remove' do
|
|
83
|
+
expect(subject[:rename_table]).to eq('simple_table' => 'simple_table_renamed')
|
|
84
|
+
expect(subject[:add_table]).to be_empty
|
|
85
|
+
expect(subject[:remove_table]).to be_empty
|
|
86
|
+
expect(subject[:change_table]).to be_empty
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
context 'when renamed_from references a table that does not exist' do
|
|
91
|
+
let(:renamed_table) do
|
|
92
|
+
Convergence::Table.new('simple_table2', renamed_from: 'nonexistent_table').tap do |t|
|
|
93
|
+
t.int('id', primary_key: true)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
let(:from_db) do
|
|
97
|
+
{ 'simple_table' => simple_table }
|
|
98
|
+
end
|
|
99
|
+
let(:to_db) do
|
|
100
|
+
{ 'simple_table' => simple_table, 'simple_table2' => renamed_table }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
subject { Convergence::Diff.new.diff(from_db, to_db) }
|
|
104
|
+
|
|
105
|
+
it 'should fall back to a normal add' do
|
|
106
|
+
expect(subject[:rename_table]).to be_empty
|
|
107
|
+
expect(subject[:add_table]['simple_table2']).not_to be_nil
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
context 'when the renamed_from target already exists (idempotent re-apply)' do
|
|
112
|
+
let(:renamed_table) do
|
|
113
|
+
Convergence::Table.new('simple_table2', renamed_from: 'simple_table').tap do |t|
|
|
114
|
+
t.int('id', primary_key: true)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
let(:from_db) do
|
|
118
|
+
{ 'simple_table' => simple_table, 'simple_table2' => simple_table2 }
|
|
119
|
+
end
|
|
120
|
+
let(:to_db) do
|
|
121
|
+
{ 'simple_table' => simple_table, 'simple_table2' => renamed_table }
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
subject { Convergence::Diff.new.diff(from_db, to_db) }
|
|
125
|
+
|
|
126
|
+
it 'should be a no-op for the rename' do
|
|
127
|
+
expect(subject[:rename_table]).to be_empty
|
|
128
|
+
expect(subject[:add_table]).to be_empty
|
|
129
|
+
expect(subject[:remove_table]).to be_empty
|
|
130
|
+
expect(subject[:change_table]).to be_empty
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
context 'when multiple tables claim the same renamed_from (ambiguous)' do
|
|
135
|
+
let(:renamed_table1) do
|
|
136
|
+
Convergence::Table.new('simple_table_a', renamed_from: 'simple_table').tap do |t|
|
|
137
|
+
t.int('id', primary_key: true)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
let(:renamed_table2) do
|
|
141
|
+
Convergence::Table.new('simple_table_b', renamed_from: 'simple_table').tap do |t|
|
|
142
|
+
t.int('id', primary_key: true)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
let(:from_db) do
|
|
146
|
+
{ 'simple_table' => simple_table }
|
|
147
|
+
end
|
|
148
|
+
let(:to_db) do
|
|
149
|
+
{ 'simple_table_a' => renamed_table1, 'simple_table_b' => renamed_table2 }
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it 'should raise an ArgumentError' do
|
|
153
|
+
expect { Convergence::Diff.new.diff(from_db, to_db) }.to raise_error(ArgumentError)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
67
157
|
context 'table column are changed' do
|
|
68
158
|
let(:table_from) do
|
|
69
159
|
Convergence::Table.new('table1').tap do |t|
|
|
@@ -104,6 +194,7 @@ describe Convergence::DSL do
|
|
|
104
194
|
let(:table_to) do
|
|
105
195
|
Convergence::Table.new('table1').tap do |t|
|
|
106
196
|
t.int('id')
|
|
197
|
+
t.varchar('name', limit: 200, null: false)
|
|
107
198
|
end
|
|
108
199
|
end
|
|
109
200
|
|
|
@@ -131,134 +222,290 @@ describe Convergence::DSL do
|
|
|
131
222
|
expect(included_after_option).to eq false
|
|
132
223
|
end
|
|
133
224
|
end
|
|
225
|
+
|
|
226
|
+
context 'all columns are renamed' do
|
|
227
|
+
let(:table_to) do
|
|
228
|
+
Convergence::Table.new('table1').tap do |t|
|
|
229
|
+
t.int('id_rename', primary_key: true, renamed_from: 'id')
|
|
230
|
+
t.varchar('name_rename', limit: 200, null: false, renamed_from: 'name')
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
it 'should detect it as a change_table with rename_column, not a drop+add' do
|
|
235
|
+
results = Convergence::Diff.new.diff({ 'table1' => table_from }, { 'table1' => table_to })
|
|
236
|
+
expect(results[:add_table]).to be_empty
|
|
237
|
+
expect(results[:remove_table]).to be_empty
|
|
238
|
+
expect(results[:change_table]).not_to be_empty
|
|
239
|
+
expect(results[:change_table]['table1'][:rename_column]).to eq('id' => 'id_rename', 'name' => 'name_rename')
|
|
240
|
+
end
|
|
241
|
+
end
|
|
134
242
|
end
|
|
135
243
|
end
|
|
136
244
|
|
|
137
245
|
describe '#diff_table' do
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
t.int('id', primary_key: true)
|
|
141
|
-
t.varchar('name', limit: 200, null: false)
|
|
246
|
+
subject(:results) { Convergence::Diff.new(**diff_options).diff_table(table_from, table_to) }
|
|
247
|
+
let(:diff_options) { {} }
|
|
142
248
|
|
|
143
|
-
|
|
144
|
-
|
|
249
|
+
context 'change auto_increment table option' do
|
|
250
|
+
let(:table_from) do
|
|
251
|
+
Convergence::Table.new('table1').tap do |t|
|
|
252
|
+
t.int('id', primary_key: true)
|
|
253
|
+
t.table_options = { auto_increment: 1 }
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
let(:table_to) do
|
|
257
|
+
Convergence::Table.new('table1').tap do |t|
|
|
258
|
+
t.int('id', primary_key: true)
|
|
259
|
+
t.table_options = { auto_increment: 5000 }
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
it 'detects the auto_increment change by default' do
|
|
264
|
+
expect(results[:change_table_option][:auto_increment]).to eq(5000)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
context 'when ignore_auto_increment is enabled' do
|
|
268
|
+
let(:diff_options) { { ignore_auto_increment: true } }
|
|
269
|
+
|
|
270
|
+
it 'ignores the auto_increment change' do
|
|
271
|
+
expect(results[:change_table_option]).not_to have_key(:auto_increment)
|
|
272
|
+
end
|
|
145
273
|
end
|
|
146
274
|
end
|
|
147
275
|
|
|
148
276
|
context 'change column options' do
|
|
277
|
+
let(:table_from) do
|
|
278
|
+
Convergence::Table.new('table1').tap do |t|
|
|
279
|
+
t.int('id', primary_key: true)
|
|
280
|
+
t.varchar('name', limit: 200, null: false)
|
|
281
|
+
t.datetime('created_at', default: -> { "CURRENT_TIMESTAMP" })
|
|
282
|
+
end
|
|
283
|
+
end
|
|
149
284
|
let(:table_to) do
|
|
150
285
|
Convergence::Table.new('table1').tap do |t|
|
|
151
286
|
t.int('id', primary_key: true)
|
|
152
|
-
t.varchar('name', limit: 300, null: true, unsigned: true)
|
|
153
|
-
|
|
154
|
-
t.index('name')
|
|
287
|
+
t.varchar('name', limit: 300, null: true, unsigned: true) # option changed
|
|
288
|
+
t.datetime('created_at', default: -> { "CURRENT_TIMESTAMP" }) # option[:default] not changed
|
|
155
289
|
end
|
|
156
290
|
end
|
|
157
291
|
|
|
158
292
|
it do
|
|
159
|
-
results = Convergence::Diff.new.diff_table(table_from, table_to)
|
|
160
293
|
expect(results[:change_column]['name']).not_to be_nil
|
|
161
294
|
expect(results[:change_column]['name'][:limit]).to eq('300')
|
|
162
295
|
expect(results[:change_column]['name'][:null]).to eq('true')
|
|
163
296
|
expect(results[:change_column]['name'][:unsigned]).to eq('true')
|
|
297
|
+
expect(results[:change_column]['created_at']).to be_nil
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
context 'change enum/set column values' do
|
|
302
|
+
let(:table_from) do
|
|
303
|
+
Convergence::Table.new('table1').tap do |t|
|
|
304
|
+
t.int('id', primary_key: true)
|
|
305
|
+
t.enum('status', values: %w(Active Inactive))
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
context 'when values are identical' do
|
|
310
|
+
let(:table_to) do
|
|
311
|
+
Convergence::Table.new('table1').tap do |t|
|
|
312
|
+
t.int('id', primary_key: true)
|
|
313
|
+
t.enum('status', values: %w(Active Inactive))
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
it 'does not detect a change' do
|
|
318
|
+
expect(results[:change_column]['status']).to be_nil
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
context 'when only the letter case differs' do
|
|
323
|
+
let(:table_to) do
|
|
324
|
+
Convergence::Table.new('table1').tap do |t|
|
|
325
|
+
t.int('id', primary_key: true)
|
|
326
|
+
t.enum('status', values: %w(active inactive))
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
it 'detects the change since values are case-sensitive' do
|
|
331
|
+
expect(results[:change_column]['status']).not_to be_nil
|
|
332
|
+
expect(results[:change_column]['status'][:values]).to eq('["active", "inactive"]')
|
|
333
|
+
end
|
|
164
334
|
end
|
|
165
335
|
end
|
|
166
336
|
|
|
167
337
|
context 'change column order' do
|
|
338
|
+
let(:table_from) do
|
|
339
|
+
Convergence::Table.new('table1').tap do |t|
|
|
340
|
+
t.int('id', primary_key: true)
|
|
341
|
+
t.varchar('name', limit: 200, null: false)
|
|
342
|
+
end
|
|
343
|
+
end
|
|
168
344
|
let(:table_to) do
|
|
169
345
|
Convergence::Table.new('table1').tap do |t|
|
|
170
|
-
t.varchar('name', limit:
|
|
346
|
+
t.varchar('name', limit: 200, null: false)
|
|
171
347
|
t.int('id', primary_key: true)
|
|
172
|
-
|
|
173
|
-
t.index('name')
|
|
174
348
|
end
|
|
175
349
|
end
|
|
176
350
|
|
|
177
351
|
it do
|
|
178
|
-
results
|
|
179
|
-
expect(results[:change_column]['name']).not_to be_nil
|
|
352
|
+
expect(results[:change_column]['name']).to be_nil
|
|
180
353
|
expect(results[:change_column]['id']).not_to be_nil
|
|
181
354
|
expect(results[:change_column]['id'][:after]).to eq('name')
|
|
182
355
|
end
|
|
183
356
|
end
|
|
184
357
|
|
|
185
|
-
context '
|
|
358
|
+
context 'rename column' do
|
|
359
|
+
let(:table_from) do
|
|
360
|
+
Convergence::Table.new('table1').tap do |t|
|
|
361
|
+
t.int('id', primary_key: true)
|
|
362
|
+
t.varchar('name', limit: 200, null: false)
|
|
363
|
+
end
|
|
364
|
+
end
|
|
186
365
|
let(:table_to) do
|
|
187
366
|
Convergence::Table.new('table1').tap do |t|
|
|
188
367
|
t.int('id', primary_key: true)
|
|
189
|
-
t.varchar('
|
|
368
|
+
t.varchar('full_name', limit: 200, null: false, renamed_from: 'name')
|
|
190
369
|
end
|
|
191
370
|
end
|
|
192
371
|
|
|
193
|
-
it do
|
|
194
|
-
results
|
|
195
|
-
expect(results[:
|
|
372
|
+
it 'should detect it as a rename, not remove+add' do
|
|
373
|
+
expect(results[:rename_column]).to eq('name' => 'full_name')
|
|
374
|
+
expect(results[:remove_column]).to be_empty
|
|
375
|
+
expect(results[:add_column]).to be_empty
|
|
196
376
|
end
|
|
197
377
|
end
|
|
198
378
|
|
|
199
|
-
context '
|
|
379
|
+
context 'rename column together with an unrelated change on another column' do
|
|
380
|
+
let(:table_from) do
|
|
381
|
+
Convergence::Table.new('table1').tap do |t|
|
|
382
|
+
t.int('id', primary_key: true)
|
|
383
|
+
t.varchar('name', limit: 200, null: false)
|
|
384
|
+
end
|
|
385
|
+
end
|
|
200
386
|
let(:table_to) do
|
|
201
387
|
Convergence::Table.new('table1').tap do |t|
|
|
202
388
|
t.int('id', primary_key: true)
|
|
203
|
-
t.varchar('
|
|
389
|
+
t.varchar('full_name', limit: 300, null: false, renamed_from: 'name')
|
|
390
|
+
end
|
|
391
|
+
end
|
|
204
392
|
|
|
205
|
-
|
|
206
|
-
|
|
393
|
+
it 'should detect only the rename, deferring the other change to the next apply pass' do
|
|
394
|
+
expect(results[:rename_column]).to eq('name' => 'full_name')
|
|
395
|
+
expect(results[:change_column]).to be_empty
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
context 'rename the only column in a table' do
|
|
400
|
+
let(:table_from) do
|
|
401
|
+
Convergence::Table.new('table1').tap do |t|
|
|
402
|
+
t.int('id', primary_key: true)
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
let(:table_to) do
|
|
406
|
+
Convergence::Table.new('table1').tap do |t|
|
|
407
|
+
t.int('identifier', primary_key: true, renamed_from: 'id')
|
|
207
408
|
end
|
|
208
409
|
end
|
|
209
410
|
|
|
210
|
-
it do
|
|
211
|
-
results
|
|
212
|
-
expect(results[:
|
|
411
|
+
it 'should not be misdetected as removing all columns' do
|
|
412
|
+
expect(results[:rename_column]).to eq('id' => 'identifier')
|
|
413
|
+
expect(results[:remove_column]).to be_empty
|
|
414
|
+
expect(results[:add_column]).to be_empty
|
|
213
415
|
end
|
|
214
416
|
end
|
|
215
417
|
|
|
216
|
-
context 'remove
|
|
418
|
+
context 'remove index' do
|
|
419
|
+
let(:table_from) do
|
|
420
|
+
Convergence::Table.new('table1').tap do |t|
|
|
421
|
+
t.int('id', primary_key: true)
|
|
422
|
+
t.varchar('name', limit: 200, null: false)
|
|
423
|
+
|
|
424
|
+
t.index('name')
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
let(:table_to) do
|
|
428
|
+
Convergence::Table.new('table1').tap do |t|
|
|
429
|
+
t.int('id', primary_key: true)
|
|
430
|
+
t.varchar('name', limit: 200, null: false)
|
|
431
|
+
end
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
it { expect(results[:remove_index].values.first.index_columns).to eq(['name']) }
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
context 'add index' do
|
|
438
|
+
let(:table_from) do
|
|
439
|
+
Convergence::Table.new('table1').tap do |t|
|
|
440
|
+
t.int('id', primary_key: true)
|
|
441
|
+
t.varchar('name', limit: 200, null: false)
|
|
442
|
+
end
|
|
443
|
+
end
|
|
217
444
|
let(:table_to) do
|
|
218
445
|
Convergence::Table.new('table1').tap do |t|
|
|
219
446
|
t.int('id', primary_key: true)
|
|
220
|
-
t.varchar('name', limit:
|
|
447
|
+
t.varchar('name', limit: 200, null: false)
|
|
221
448
|
|
|
222
449
|
t.index('name')
|
|
223
450
|
end
|
|
224
451
|
end
|
|
225
452
|
|
|
226
|
-
it
|
|
227
|
-
|
|
228
|
-
|
|
453
|
+
it { expect(results[:add_index].values.first.index_columns).to eq(['name']) }
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
context 'remove foreign key' do
|
|
457
|
+
let(:table_from) do
|
|
458
|
+
Convergence::Table.new('table1').tap do |t|
|
|
459
|
+
t.int('id', primary_key: true)
|
|
460
|
+
t.varchar('name', limit: 200, null: false)
|
|
461
|
+
|
|
462
|
+
t.foreign_key('id', reference: 'ref_tables', reference_column: 'ref_id')
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
let(:table_to) do
|
|
466
|
+
Convergence::Table.new('table1').tap do |t|
|
|
467
|
+
t.int('id', primary_key: true)
|
|
468
|
+
t.varchar('name', limit: 200, null: false)
|
|
469
|
+
end
|
|
229
470
|
end
|
|
471
|
+
|
|
472
|
+
it { expect(results[:remove_foreign_key].values.first.from_columns).to eq(['id']) }
|
|
230
473
|
end
|
|
231
474
|
|
|
232
475
|
context 'add foreign key' do
|
|
476
|
+
let(:table_from) do
|
|
477
|
+
Convergence::Table.new('table1').tap do |t|
|
|
478
|
+
t.int('id', primary_key: true)
|
|
479
|
+
t.varchar('name', limit: 200, null: false)
|
|
480
|
+
end
|
|
481
|
+
end
|
|
233
482
|
let(:table_to) do
|
|
234
483
|
Convergence::Table.new('table1').tap do |t|
|
|
235
484
|
t.int('id', primary_key: true)
|
|
236
|
-
t.varchar('name', limit:
|
|
485
|
+
t.varchar('name', limit: 200, null: false)
|
|
237
486
|
|
|
238
|
-
t.index('name')
|
|
239
487
|
t.foreign_key('id', reference: 'ref_tables', reference_column: 'ref_id')
|
|
240
|
-
t.foreign_key('id2', reference: 'ref_tables2', reference_column: 'ref_id2')
|
|
241
488
|
end
|
|
242
489
|
end
|
|
243
490
|
|
|
244
|
-
it
|
|
245
|
-
results = Convergence::Diff.new.diff_table(table_from, table_to)
|
|
246
|
-
expect(results[:add_foreign_key].values.first.from_columns).to eq(['id2'])
|
|
247
|
-
end
|
|
491
|
+
it { expect(results[:add_foreign_key].values.first.from_columns).to eq(['id']) }
|
|
248
492
|
end
|
|
249
493
|
|
|
250
494
|
context 'change table options' do
|
|
495
|
+
let(:table_from) do
|
|
496
|
+
Convergence::Table.new('table1').tap do |t|
|
|
497
|
+
t.int('id', primary_key: true)
|
|
498
|
+
t.varchar('name', limit: 200, null: false)
|
|
499
|
+
end
|
|
500
|
+
end
|
|
251
501
|
let(:table_to) do
|
|
252
502
|
Convergence::Table.new('table1', engine: 'MyISAM').tap do |t|
|
|
253
|
-
t.varchar('name', limit: 300, null: true)
|
|
254
503
|
t.int('id', primary_key: true)
|
|
504
|
+
t.varchar('name', limit: 200, null: false)
|
|
255
505
|
end
|
|
256
506
|
end
|
|
257
507
|
|
|
258
|
-
it
|
|
259
|
-
results = Convergence::Diff.new.diff_table(table_from, table_to)
|
|
260
|
-
expect(results[:change_table_option][:engine]).to eq('MyISAM')
|
|
261
|
-
end
|
|
508
|
+
it { expect(results[:change_table_option][:engine]).to eq('MyISAM') }
|
|
262
509
|
end
|
|
263
510
|
end
|
|
264
511
|
end
|
|
@@ -89,4 +89,30 @@ describe Convergence::DSL do
|
|
|
89
89
|
end
|
|
90
90
|
end
|
|
91
91
|
end
|
|
92
|
+
|
|
93
|
+
describe '#parse_dsl' do
|
|
94
|
+
let(:dsl_with_execute) do
|
|
95
|
+
<<-DSL
|
|
96
|
+
create_table "users" do |t|
|
|
97
|
+
t.int "id", primary_key: true, extra: "auto_increment"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
execute "CREATE OR REPLACE VIEW active_users AS SELECT * FROM users"
|
|
101
|
+
execute "GRANT SELECT ON users TO readonly"
|
|
102
|
+
DSL
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
subject { Convergence::DSL.parse_dsl(dsl_with_execute, '') }
|
|
106
|
+
|
|
107
|
+
it 'should be able to parse tables' do
|
|
108
|
+
expect(subject.tables['users']).not_to be_nil
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it 'should be able to collect raw sql statements in order' do
|
|
112
|
+
expect(subject.raw_sqls).to eq([
|
|
113
|
+
'CREATE OR REPLACE VIEW active_users AS SELECT * FROM users',
|
|
114
|
+
'GRANT SELECT ON users TO readonly'
|
|
115
|
+
])
|
|
116
|
+
end
|
|
117
|
+
end
|
|
92
118
|
end
|
|
@@ -37,14 +37,17 @@ describe Convergence::Dumper::MysqlSchemaDumper do
|
|
|
37
37
|
expect(papers.columns['title1']).not_to be_nil
|
|
38
38
|
expect(papers.columns['title2']).not_to be_nil
|
|
39
39
|
expect(papers.columns['description']).not_to be_nil
|
|
40
|
+
expect(papers.columns['edition_number']).not_to be_nil
|
|
41
|
+
expect(papers.columns['published_at']).not_to be_nil
|
|
40
42
|
end
|
|
41
43
|
|
|
42
44
|
it 'should be dump columns in the correct order' do
|
|
43
45
|
papers = subject['papers']
|
|
44
|
-
expect(papers.columns.keys)
|
|
46
|
+
expect(papers.columns.keys)
|
|
47
|
+
.to eq(%w(id slug title1 title2 description edition_number published_at))
|
|
45
48
|
end
|
|
46
49
|
|
|
47
|
-
describe 'table options' do
|
|
50
|
+
describe 'table column options' do
|
|
48
51
|
it 'should be dump primary key' do
|
|
49
52
|
expect(subject['papers'].columns['id'].options[:primary_key]).to be_truthy
|
|
50
53
|
end
|
|
@@ -69,6 +72,13 @@ describe Convergence::Dumper::MysqlSchemaDumper do
|
|
|
69
72
|
it 'should be dump unsigned definition' do
|
|
70
73
|
expect(subject['authors'].columns['age'].options[:unsigned]).to be_truthy
|
|
71
74
|
end
|
|
75
|
+
|
|
76
|
+
it 'should be dump default' do
|
|
77
|
+
expect(subject['papers'].columns['edition_number'].options[:default]).to eq "0"
|
|
78
|
+
expect(subject['papers'].columns['published_at'].options[:default])
|
|
79
|
+
.to be_a(Proc)
|
|
80
|
+
.and have_attributes(call: "CURRENT_TIMESTAMP")
|
|
81
|
+
end
|
|
72
82
|
end
|
|
73
83
|
end
|
|
74
84
|
|
|
@@ -96,6 +106,19 @@ describe Convergence::Dumper::MysqlSchemaDumper do
|
|
|
96
106
|
end
|
|
97
107
|
end
|
|
98
108
|
|
|
109
|
+
describe 'enum/set columns' do
|
|
110
|
+
it 'should be dump enum values' do
|
|
111
|
+
expect(subject['enum_set_samples'].columns['status'].options[:values])
|
|
112
|
+
.to eq(%w(active inactive pending))
|
|
113
|
+
expect(subject['enum_set_samples'].columns['status'].options[:default]).to eq('active')
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it 'should be dump set values, including values containing quotes' do
|
|
117
|
+
expect(subject['enum_set_samples'].columns['flags'].options[:values])
|
|
118
|
+
.to eq(['a', 'b', "it's tricky"])
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
99
122
|
describe 'foreign keys' do
|
|
100
123
|
it do
|
|
101
124
|
foreign_key = subject['paper_authors'].foreign_keys['paper_authors_author_id_fk']
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'convergence/dumper/postgres_schema_dumper'
|
|
3
|
+
require 'convergence/database_connector'
|
|
4
|
+
|
|
5
|
+
describe Convergence::Dumper::PostgresSchemaDumper do
|
|
6
|
+
let(:connector) do
|
|
7
|
+
Convergence::DatabaseConnector.new(postgres_settings)
|
|
8
|
+
end
|
|
9
|
+
let(:dumper) do
|
|
10
|
+
Convergence::Dumper::PostgresSchemaDumper.new(connector)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe '#dump' do
|
|
14
|
+
subject { dumper.dump }
|
|
15
|
+
|
|
16
|
+
it 'should be dump tables' do
|
|
17
|
+
expect(subject['papers']).not_to be_nil
|
|
18
|
+
expect(subject['authors']).not_to be_nil
|
|
19
|
+
expect(subject['paper_authors']).not_to be_nil
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe 'table options' do
|
|
23
|
+
it 'should be dump comment' do
|
|
24
|
+
expect(subject['papers'].table_options[:comment]).to eq('Paper')
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
describe 'table columns' do
|
|
29
|
+
it 'should be dump table columns' do
|
|
30
|
+
papers = subject['papers']
|
|
31
|
+
expect(papers.columns['id']).not_to be_nil
|
|
32
|
+
expect(papers.columns['slug']).not_to be_nil
|
|
33
|
+
expect(papers.columns['title1']).not_to be_nil
|
|
34
|
+
expect(papers.columns['title2']).not_to be_nil
|
|
35
|
+
expect(papers.columns['description']).not_to be_nil
|
|
36
|
+
expect(papers.columns['edition_number']).not_to be_nil
|
|
37
|
+
expect(papers.columns['published_at']).not_to be_nil
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'should be dump columns in the correct order' do
|
|
41
|
+
papers = subject['papers']
|
|
42
|
+
expect(papers.columns.keys)
|
|
43
|
+
.to eq(%w(id slug title1 title2 description edition_number published_at))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe 'table column options' do
|
|
47
|
+
it 'should be dump primary key' do
|
|
48
|
+
expect(subject['papers'].columns['id'].options[:primary_key]).to be_truthy
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'should be dump extra (identity columns as auto_increment)' do
|
|
52
|
+
expect(subject['papers'].columns['id'].options[:extra]).to eq('auto_increment')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'should be dump comment' do
|
|
56
|
+
expect(subject['papers'].columns['title1'].options[:comment]).to eq('Title 1')
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'should be dump limit' do
|
|
60
|
+
expect(subject['papers'].columns['title1'].options[:limit]).to eq('300')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'should be dump not null definition' do
|
|
64
|
+
expect(subject['papers'].columns['title1'].options[:null]).to be_falsy
|
|
65
|
+
expect(subject['authors'].columns['created_at'].options[:null]).to be_truthy
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'should be dump default' do
|
|
69
|
+
expect(subject['papers'].columns['edition_number'].options[:default]).to eq('0')
|
|
70
|
+
expect(subject['papers'].columns['published_at'].options[:default])
|
|
71
|
+
.to be_a(Proc)
|
|
72
|
+
.and have_attributes(call: 'CURRENT_TIMESTAMP')
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
describe 'indexes' do
|
|
78
|
+
it 'should be dump index of authors' do
|
|
79
|
+
index = subject['authors'].indexes['index_authors_on_created_at']
|
|
80
|
+
expect(index).not_to be_nil
|
|
81
|
+
expect(index.index_columns).to eq(['created_at'])
|
|
82
|
+
expect(index.options[:unique]).to eq(false)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'should be dump unique index of papers' do
|
|
86
|
+
index = subject['papers'].indexes['index_papers_on_slug']
|
|
87
|
+
expect(index).not_to be_nil
|
|
88
|
+
expect(index.index_columns).to eq(['slug'])
|
|
89
|
+
expect(index.options[:unique]).to eq(true)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe 'foreign keys' do
|
|
94
|
+
it do
|
|
95
|
+
foreign_key = subject['paper_authors'].foreign_keys['paper_authors_author_id_fk']
|
|
96
|
+
expect(foreign_key).not_to be_nil
|
|
97
|
+
expect(foreign_key.from_columns).to eq(['author_id'])
|
|
98
|
+
expect(foreign_key.to_table).to eq('authors')
|
|
99
|
+
expect(foreign_key.to_columns).to eq(['id'])
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|