convergence 1.0.5 → 1.1.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 +55 -0
- data/.gitignore +1 -0
- data/CHANGELOG.md +52 -0
- data/CONTRIBUTING.md +80 -0
- data/Gemfile.lock +24 -9
- data/README.md +131 -1
- data/Rakefile +48 -5
- data/convergence.gemspec +3 -1
- data/lib/convergence/cli.rb +10 -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/config.rb +1 -1
- 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 +9 -2
- 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 +18 -3
- data/lib/convergence/sql_generator/postgres_generator.rb +256 -0
- data/lib/convergence/sql_generator/sqlite_generator.rb +178 -0
- data/lib/convergence/version.rb +1 -1
- data/spec/config/spec_database.yml +15 -2
- data/spec/convergence/config_spec.rb +75 -0
- data/spec/convergence/diff_spec.rb +125 -45
- 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 +23 -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_test_db.sql +41 -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_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 +37 -2
- data/spec/integrations/command_export.rb +28 -0
- data/spec/postgres_integrations/command_dryrun.rb +79 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/sqlite_integrations/command_dryrun.rb +70 -0
- metadata +97 -9
- data/.travis.yml +0 -9
|
@@ -104,6 +104,7 @@ describe Convergence::DSL do
|
|
|
104
104
|
let(:table_to) do
|
|
105
105
|
Convergence::Table.new('table1').tap do |t|
|
|
106
106
|
t.int('id')
|
|
107
|
+
t.varchar('name', limit: 200, null: false)
|
|
107
108
|
end
|
|
108
109
|
end
|
|
109
110
|
|
|
@@ -135,130 +136,209 @@ describe Convergence::DSL do
|
|
|
135
136
|
end
|
|
136
137
|
|
|
137
138
|
describe '#diff_table' do
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
subject(:results) { Convergence::Diff.new(**diff_options).diff_table(table_from, table_to) }
|
|
140
|
+
let(:diff_options) { {} }
|
|
141
|
+
|
|
142
|
+
context 'change auto_increment table option' do
|
|
143
|
+
let(:table_from) do
|
|
144
|
+
Convergence::Table.new('table1').tap do |t|
|
|
145
|
+
t.int('id', primary_key: true)
|
|
146
|
+
t.table_options = { auto_increment: 1 }
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
let(:table_to) do
|
|
150
|
+
Convergence::Table.new('table1').tap do |t|
|
|
151
|
+
t.int('id', primary_key: true)
|
|
152
|
+
t.table_options = { auto_increment: 5000 }
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it 'detects the auto_increment change by default' do
|
|
157
|
+
expect(results[:change_table_option][:auto_increment]).to eq(5000)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
context 'when ignore_auto_increment is enabled' do
|
|
161
|
+
let(:diff_options) { { ignore_auto_increment: true } }
|
|
142
162
|
|
|
143
|
-
|
|
144
|
-
|
|
163
|
+
it 'ignores the auto_increment change' do
|
|
164
|
+
expect(results[:change_table_option]).not_to have_key(:auto_increment)
|
|
165
|
+
end
|
|
145
166
|
end
|
|
146
167
|
end
|
|
147
168
|
|
|
148
169
|
context 'change column options' do
|
|
170
|
+
let(:table_from) do
|
|
171
|
+
Convergence::Table.new('table1').tap do |t|
|
|
172
|
+
t.int('id', primary_key: true)
|
|
173
|
+
t.varchar('name', limit: 200, null: false)
|
|
174
|
+
t.datetime('created_at', default: -> { "CURRENT_TIMESTAMP" })
|
|
175
|
+
end
|
|
176
|
+
end
|
|
149
177
|
let(:table_to) do
|
|
150
178
|
Convergence::Table.new('table1').tap do |t|
|
|
151
179
|
t.int('id', primary_key: true)
|
|
152
|
-
t.varchar('name', limit: 300, null: true, unsigned: true)
|
|
153
|
-
|
|
154
|
-
t.index('name')
|
|
180
|
+
t.varchar('name', limit: 300, null: true, unsigned: true) # option changed
|
|
181
|
+
t.datetime('created_at', default: -> { "CURRENT_TIMESTAMP" }) # option[:default] not changed
|
|
155
182
|
end
|
|
156
183
|
end
|
|
157
184
|
|
|
158
185
|
it do
|
|
159
|
-
results = Convergence::Diff.new.diff_table(table_from, table_to)
|
|
160
186
|
expect(results[:change_column]['name']).not_to be_nil
|
|
161
187
|
expect(results[:change_column]['name'][:limit]).to eq('300')
|
|
162
188
|
expect(results[:change_column]['name'][:null]).to eq('true')
|
|
163
189
|
expect(results[:change_column]['name'][:unsigned]).to eq('true')
|
|
190
|
+
expect(results[:change_column]['created_at']).to be_nil
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
context 'change enum/set column values' do
|
|
195
|
+
let(:table_from) do
|
|
196
|
+
Convergence::Table.new('table1').tap do |t|
|
|
197
|
+
t.int('id', primary_key: true)
|
|
198
|
+
t.enum('status', values: %w(Active Inactive))
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
context 'when values are identical' do
|
|
203
|
+
let(:table_to) do
|
|
204
|
+
Convergence::Table.new('table1').tap do |t|
|
|
205
|
+
t.int('id', primary_key: true)
|
|
206
|
+
t.enum('status', values: %w(Active Inactive))
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
it 'does not detect a change' do
|
|
211
|
+
expect(results[:change_column]['status']).to be_nil
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
context 'when only the letter case differs' do
|
|
216
|
+
let(:table_to) do
|
|
217
|
+
Convergence::Table.new('table1').tap do |t|
|
|
218
|
+
t.int('id', primary_key: true)
|
|
219
|
+
t.enum('status', values: %w(active inactive))
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
it 'detects the change since values are case-sensitive' do
|
|
224
|
+
expect(results[:change_column]['status']).not_to be_nil
|
|
225
|
+
expect(results[:change_column]['status'][:values]).to eq('["active", "inactive"]')
|
|
226
|
+
end
|
|
164
227
|
end
|
|
165
228
|
end
|
|
166
229
|
|
|
167
230
|
context 'change column order' do
|
|
231
|
+
let(:table_from) do
|
|
232
|
+
Convergence::Table.new('table1').tap do |t|
|
|
233
|
+
t.int('id', primary_key: true)
|
|
234
|
+
t.varchar('name', limit: 200, null: false)
|
|
235
|
+
end
|
|
236
|
+
end
|
|
168
237
|
let(:table_to) do
|
|
169
238
|
Convergence::Table.new('table1').tap do |t|
|
|
170
|
-
t.varchar('name', limit:
|
|
239
|
+
t.varchar('name', limit: 200, null: false)
|
|
171
240
|
t.int('id', primary_key: true)
|
|
172
|
-
|
|
173
|
-
t.index('name')
|
|
174
241
|
end
|
|
175
242
|
end
|
|
176
243
|
|
|
177
244
|
it do
|
|
178
|
-
results
|
|
179
|
-
expect(results[:change_column]['name']).not_to be_nil
|
|
245
|
+
expect(results[:change_column]['name']).to be_nil
|
|
180
246
|
expect(results[:change_column]['id']).not_to be_nil
|
|
181
247
|
expect(results[:change_column]['id'][:after]).to eq('name')
|
|
182
248
|
end
|
|
183
249
|
end
|
|
184
250
|
|
|
185
251
|
context 'remove index' do
|
|
252
|
+
let(:table_from) do
|
|
253
|
+
Convergence::Table.new('table1').tap do |t|
|
|
254
|
+
t.int('id', primary_key: true)
|
|
255
|
+
t.varchar('name', limit: 200, null: false)
|
|
256
|
+
|
|
257
|
+
t.index('name')
|
|
258
|
+
end
|
|
259
|
+
end
|
|
186
260
|
let(:table_to) do
|
|
187
261
|
Convergence::Table.new('table1').tap do |t|
|
|
188
262
|
t.int('id', primary_key: true)
|
|
189
|
-
t.varchar('name', limit:
|
|
263
|
+
t.varchar('name', limit: 200, null: false)
|
|
190
264
|
end
|
|
191
265
|
end
|
|
192
266
|
|
|
193
|
-
it
|
|
194
|
-
results = Convergence::Diff.new.diff_table(table_from, table_to)
|
|
195
|
-
expect(results[:remove_index].values.first.index_columns).to eq(['name'])
|
|
196
|
-
end
|
|
267
|
+
it { expect(results[:remove_index].values.first.index_columns).to eq(['name']) }
|
|
197
268
|
end
|
|
198
269
|
|
|
199
270
|
context 'add index' do
|
|
271
|
+
let(:table_from) do
|
|
272
|
+
Convergence::Table.new('table1').tap do |t|
|
|
273
|
+
t.int('id', primary_key: true)
|
|
274
|
+
t.varchar('name', limit: 200, null: false)
|
|
275
|
+
end
|
|
276
|
+
end
|
|
200
277
|
let(:table_to) do
|
|
201
278
|
Convergence::Table.new('table1').tap do |t|
|
|
202
279
|
t.int('id', primary_key: true)
|
|
203
|
-
t.varchar('name', limit:
|
|
280
|
+
t.varchar('name', limit: 200, null: false)
|
|
204
281
|
|
|
205
|
-
t.index('id')
|
|
206
282
|
t.index('name')
|
|
207
283
|
end
|
|
208
284
|
end
|
|
209
285
|
|
|
210
|
-
it
|
|
211
|
-
results = Convergence::Diff.new.diff_table(table_from, table_to)
|
|
212
|
-
expect(results[:add_index].values.first.index_columns).to eq(['id'])
|
|
213
|
-
end
|
|
286
|
+
it { expect(results[:add_index].values.first.index_columns).to eq(['name']) }
|
|
214
287
|
end
|
|
215
288
|
|
|
216
289
|
context 'remove foreign key' do
|
|
217
|
-
let(:
|
|
290
|
+
let(:table_from) do
|
|
218
291
|
Convergence::Table.new('table1').tap do |t|
|
|
219
292
|
t.int('id', primary_key: true)
|
|
220
|
-
t.varchar('name', limit:
|
|
293
|
+
t.varchar('name', limit: 200, null: false)
|
|
221
294
|
|
|
222
|
-
t.
|
|
295
|
+
t.foreign_key('id', reference: 'ref_tables', reference_column: 'ref_id')
|
|
223
296
|
end
|
|
224
297
|
end
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
298
|
+
let(:table_to) do
|
|
299
|
+
Convergence::Table.new('table1').tap do |t|
|
|
300
|
+
t.int('id', primary_key: true)
|
|
301
|
+
t.varchar('name', limit: 200, null: false)
|
|
302
|
+
end
|
|
229
303
|
end
|
|
304
|
+
|
|
305
|
+
it { expect(results[:remove_foreign_key].values.first.from_columns).to eq(['id']) }
|
|
230
306
|
end
|
|
231
307
|
|
|
232
308
|
context 'add foreign key' do
|
|
309
|
+
let(:table_from) do
|
|
310
|
+
Convergence::Table.new('table1').tap do |t|
|
|
311
|
+
t.int('id', primary_key: true)
|
|
312
|
+
t.varchar('name', limit: 200, null: false)
|
|
313
|
+
end
|
|
314
|
+
end
|
|
233
315
|
let(:table_to) do
|
|
234
316
|
Convergence::Table.new('table1').tap do |t|
|
|
235
317
|
t.int('id', primary_key: true)
|
|
236
|
-
t.varchar('name', limit:
|
|
318
|
+
t.varchar('name', limit: 200, null: false)
|
|
237
319
|
|
|
238
|
-
t.index('name')
|
|
239
320
|
t.foreign_key('id', reference: 'ref_tables', reference_column: 'ref_id')
|
|
240
|
-
t.foreign_key('id2', reference: 'ref_tables2', reference_column: 'ref_id2')
|
|
241
321
|
end
|
|
242
322
|
end
|
|
243
323
|
|
|
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
|
|
324
|
+
it { expect(results[:add_foreign_key].values.first.from_columns).to eq(['id']) }
|
|
248
325
|
end
|
|
249
326
|
|
|
250
327
|
context 'change table options' do
|
|
328
|
+
let(:table_from) do
|
|
329
|
+
Convergence::Table.new('table1').tap do |t|
|
|
330
|
+
t.int('id', primary_key: true)
|
|
331
|
+
t.varchar('name', limit: 200, null: false)
|
|
332
|
+
end
|
|
333
|
+
end
|
|
251
334
|
let(:table_to) do
|
|
252
335
|
Convergence::Table.new('table1', engine: 'MyISAM').tap do |t|
|
|
253
|
-
t.varchar('name', limit: 300, null: true)
|
|
254
336
|
t.int('id', primary_key: true)
|
|
337
|
+
t.varchar('name', limit: 200, null: false)
|
|
255
338
|
end
|
|
256
339
|
end
|
|
257
340
|
|
|
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
|
|
341
|
+
it { expect(results[:change_table_option][:engine]).to eq('MyISAM') }
|
|
262
342
|
end
|
|
263
343
|
end
|
|
264
344
|
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
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'convergence/dumper/sqlite_schema_dumper'
|
|
3
|
+
require 'convergence/database_connector'
|
|
4
|
+
|
|
5
|
+
describe Convergence::Dumper::SqliteSchemaDumper do
|
|
6
|
+
let(:connector) do
|
|
7
|
+
Convergence::DatabaseConnector.new(sqlite_settings)
|
|
8
|
+
end
|
|
9
|
+
let(:dumper) do
|
|
10
|
+
Convergence::Dumper::SqliteSchemaDumper.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 columns' do
|
|
23
|
+
it 'should be dump table columns' do
|
|
24
|
+
papers = subject['papers']
|
|
25
|
+
expect(papers.columns['id']).not_to be_nil
|
|
26
|
+
expect(papers.columns['slug']).not_to be_nil
|
|
27
|
+
expect(papers.columns['title1']).not_to be_nil
|
|
28
|
+
expect(papers.columns['title2']).not_to be_nil
|
|
29
|
+
expect(papers.columns['description']).not_to be_nil
|
|
30
|
+
expect(papers.columns['edition_number']).not_to be_nil
|
|
31
|
+
expect(papers.columns['published_at']).not_to be_nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe 'table column options' do
|
|
35
|
+
it 'should be dump primary key as auto_increment' do
|
|
36
|
+
expect(subject['papers'].columns['id'].options[:primary_key]).to be_truthy
|
|
37
|
+
expect(subject['papers'].columns['id'].options[:extra]).to eq('auto_increment')
|
|
38
|
+
expect(subject['papers'].columns['id'].options[:null]).to be_falsy
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'should be dump limit' do
|
|
42
|
+
expect(subject['papers'].columns['title1'].options[:limit]).to eq('300')
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'should be dump not null definition' do
|
|
46
|
+
expect(subject['papers'].columns['title1'].options[:null]).to be_falsy
|
|
47
|
+
expect(subject['authors'].columns['created_at'].options[:null]).to be_truthy
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'should be dump default' do
|
|
51
|
+
expect(subject['papers'].columns['edition_number'].options[:default]).to eq('0')
|
|
52
|
+
expect(subject['papers'].columns['published_at'].options[:default])
|
|
53
|
+
.to be_a(Proc)
|
|
54
|
+
.and have_attributes(call: 'CURRENT_TIMESTAMP')
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
describe 'indexes' do
|
|
60
|
+
it 'should be dump index of authors' do
|
|
61
|
+
index = subject['authors'].indexes['index_authors_on_created_at']
|
|
62
|
+
expect(index).not_to be_nil
|
|
63
|
+
expect(index.index_columns).to eq(['created_at'])
|
|
64
|
+
expect(index.options[:unique]).to eq(false)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'should be dump unique index of papers' do
|
|
68
|
+
index = subject['papers'].indexes['index_papers_on_slug']
|
|
69
|
+
expect(index).not_to be_nil
|
|
70
|
+
expect(index.index_columns).to eq(['slug'])
|
|
71
|
+
expect(index.options[:unique]).to eq(true)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
describe 'foreign keys' do
|
|
76
|
+
it 'should be dump the foreign key with its original constraint name' do
|
|
77
|
+
foreign_key = subject['paper_authors'].foreign_keys['paper_authors_author_id_fk']
|
|
78
|
+
expect(foreign_key).not_to be_nil
|
|
79
|
+
expect(foreign_key.from_columns).to eq(['author_id'])
|
|
80
|
+
expect(foreign_key.to_table).to eq('authors')
|
|
81
|
+
expect(foreign_key.to_columns).to eq(['id'])
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -13,15 +13,15 @@ describe Convergence::Dumper do
|
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
let(:table1_dsl) do
|
|
16
|
-
dsl =
|
|
17
|
-
create_table :dummy_table, engine: "MyISAM" do |t|
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
dsl = <<~DSL
|
|
17
|
+
create_table :dummy_table, engine: "MyISAM" do |t|
|
|
18
|
+
t.int :id, limit: 11
|
|
19
|
+
t.varchar :name, limit: 100, null: true, comment: "name"
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
end
|
|
24
|
-
|
|
21
|
+
t.index :name, name: "idx_name"
|
|
22
|
+
t.foreign_key :id, reference: :dummy_ref, reference_column: :id, name: "dummy_table_id_fk"
|
|
23
|
+
end
|
|
24
|
+
DSL
|
|
25
25
|
dsl.strip
|
|
26
26
|
end
|
|
27
27
|
|
|
@@ -51,15 +51,15 @@ end
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
let(:table1_dsl) do
|
|
54
|
-
dsl =
|
|
55
|
-
create_table :"dummy-table", engine: "MyISAM" do |t|
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
dsl = <<~DSL
|
|
55
|
+
create_table :"dummy-table", engine: "MyISAM" do |t|
|
|
56
|
+
t.int :id, limit: 11
|
|
57
|
+
t.varchar :"column-1", limit: 100, null: true, comment: "column 1"
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
end
|
|
62
|
-
|
|
59
|
+
t.index :"column-1", name: "idx_column-1"
|
|
60
|
+
t.foreign_key :"column-1", reference: :"dummy-ref", reference_column: :"dummy-column", name: "dummy-table_column-1_fk"
|
|
61
|
+
end
|
|
62
|
+
DSL
|
|
63
63
|
dsl.strip
|
|
64
64
|
end
|
|
65
65
|
|
|
@@ -68,5 +68,99 @@ end
|
|
|
68
68
|
expect(dsl).to eq(table1_dsl)
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
|
+
|
|
72
|
+
context "when the table column has default option with proc" do
|
|
73
|
+
let(:table1) do
|
|
74
|
+
Convergence::Table.new('dummy_table').tap do |t|
|
|
75
|
+
t.int :edition_number, default: 0
|
|
76
|
+
t.datetime :created_at, default: -> { "CURRENT_TIMESTAMP" }
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
let(:table1_dsl) do
|
|
81
|
+
dsl = <<~DSL
|
|
82
|
+
create_table :dummy_table do |t|
|
|
83
|
+
t.int :edition_number, default: 0
|
|
84
|
+
t.datetime :created_at, default: -> { "CURRENT_TIMESTAMP" }
|
|
85
|
+
end
|
|
86
|
+
DSL
|
|
87
|
+
dsl.strip
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it 'should be able to dump dsl' do
|
|
91
|
+
dsl = Convergence::Dumper.new.dump_table_dsl(table1)
|
|
92
|
+
expect(dsl).to eq(table1_dsl)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe '#dump_rails_migration' do
|
|
98
|
+
let(:table1) do
|
|
99
|
+
Convergence::Table.new('dummy_table', engine: 'MyISAM').tap do |t|
|
|
100
|
+
t.int :id, primary_key: true, extra: 'auto_increment'
|
|
101
|
+
t.varchar :name, limit: 100, null: true, comment: 'name'
|
|
102
|
+
t.datetime :created_at, null: false
|
|
103
|
+
t.datetime :updated_at, null: false
|
|
104
|
+
|
|
105
|
+
t.index :name, name: 'idx_name'
|
|
106
|
+
t.foreign_key :id, reference: 'dummy_ref', reference_column: :id
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
let(:migration_dsl) do
|
|
111
|
+
dsl = <<~DSL
|
|
112
|
+
class DummyMigration < ActiveRecord::Migration[7.0]
|
|
113
|
+
def change
|
|
114
|
+
create_table :dummy_table do |t|
|
|
115
|
+
t.string :name, limit: 100, null: true, comment: "name"
|
|
116
|
+
t.timestamps null: false
|
|
117
|
+
t.index :name, name: "idx_name"
|
|
118
|
+
t.foreign_key :dummy_ref, column: :id, name: "dummy_table_id_fk"
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
DSL
|
|
123
|
+
dsl.strip
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it 'omits the implicit auto-increment id column' do
|
|
127
|
+
migration = Convergence::Dumper.new.dump_rails_migration({ 'dummy_table' => table1 }, 'DummyMigration')
|
|
128
|
+
expect(migration).to eq(migration_dsl)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it 'collapses created_at/updated_at into t.timestamps' do
|
|
132
|
+
migration = Convergence::Dumper.new.dump_rails_migration({ 'dummy_table' => table1 }, 'DummyMigration')
|
|
133
|
+
expect(migration).to be_include('t.timestamps null: false')
|
|
134
|
+
expect(migration).not_to be_include('t.datetime :created_at')
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
context 'when the column is a MySQL tinyint(1) boolean' do
|
|
138
|
+
let(:table1) do
|
|
139
|
+
Convergence::Table.new('dummy_table').tap do |t|
|
|
140
|
+
t.boolean :active, default: true
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it 'maps it to a native boolean column' do
|
|
145
|
+
migration = Convergence::Dumper.new.dump_rails_migration({ 'dummy_table' => table1 }, 'DummyMigration')
|
|
146
|
+
expect(migration).to be_include('t.boolean :active, default: true')
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
context 'when created_at/updated_at have mismatched null options' do
|
|
151
|
+
let(:table1) do
|
|
152
|
+
Convergence::Table.new('dummy_table').tap do |t|
|
|
153
|
+
t.datetime :created_at, null: false
|
|
154
|
+
t.datetime :updated_at, null: true
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it 'keeps them as separate columns instead of collapsing into t.timestamps' do
|
|
159
|
+
migration = Convergence::Dumper.new.dump_rails_migration({ 'dummy_table' => table1 }, 'DummyMigration')
|
|
160
|
+
expect(migration).not_to be_include('t.timestamps')
|
|
161
|
+
expect(migration).to be_include('t.datetime :created_at, null: false')
|
|
162
|
+
expect(migration).to be_include('t.datetime :updated_at, null: true')
|
|
163
|
+
end
|
|
164
|
+
end
|
|
71
165
|
end
|
|
72
166
|
end
|