chrono_model 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +2 -1
  3. data/lib/chrono_model/time_machine.rb +1 -1
  4. data/lib/chrono_model/time_machine/history_model.rb +4 -0
  5. data/lib/chrono_model/version.rb +1 -1
  6. data/spec/chrono_model/adapter/base_spec.rb +157 -0
  7. data/spec/chrono_model/adapter/ddl_spec.rb +243 -0
  8. data/spec/chrono_model/adapter/indexes_spec.rb +72 -0
  9. data/spec/chrono_model/adapter/migrations_spec.rb +312 -0
  10. data/spec/chrono_model/history_models_spec.rb +32 -0
  11. data/spec/chrono_model/time_machine/as_of_spec.rb +188 -0
  12. data/spec/chrono_model/time_machine/changes_spec.rb +50 -0
  13. data/spec/chrono_model/{adapter → time_machine}/counter_cache_race_spec.rb +2 -2
  14. data/spec/chrono_model/time_machine/default_scope_spec.rb +37 -0
  15. data/spec/chrono_model/time_machine/history_spec.rb +104 -0
  16. data/spec/chrono_model/time_machine/keep_cool_spec.rb +27 -0
  17. data/spec/chrono_model/time_machine/manipulations_spec.rb +84 -0
  18. data/spec/chrono_model/time_machine/model_identification_spec.rb +46 -0
  19. data/spec/chrono_model/time_machine/sequence_spec.rb +74 -0
  20. data/spec/chrono_model/time_machine/sti_spec.rb +100 -0
  21. data/spec/chrono_model/{time_query_spec.rb → time_machine/time_query_spec.rb} +22 -5
  22. data/spec/chrono_model/time_machine/timeline_spec.rb +63 -0
  23. data/spec/chrono_model/time_machine/timestamps_spec.rb +43 -0
  24. data/spec/chrono_model/time_machine/transactions_spec.rb +69 -0
  25. data/spec/support/adapter/helpers.rb +53 -0
  26. data/spec/support/adapter/structure.rb +44 -0
  27. data/spec/support/time_machine/helpers.rb +47 -0
  28. data/spec/support/time_machine/structure.rb +111 -0
  29. metadata +48 -14
  30. data/spec/chrono_model/adapter/sti_bug_spec.rb +0 -49
  31. data/spec/chrono_model/adapter_spec.rb +0 -788
  32. data/spec/chrono_model/time_machine_spec.rb +0 -749
  33. data/spec/support/helpers.rb +0 -198
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chrono_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcello Barnaba
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-04-13 00:00:00.000000000 Z
12
+ date: 2019-04-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -252,25 +252,42 @@ files:
252
252
  - spec/aruba/fixtures/railsapp/config/environments/development.rb
253
253
  - spec/aruba/migrations_spec.rb
254
254
  - spec/aruba/rake_task_spec.rb
255
- - spec/chrono_model/adapter/counter_cache_race_spec.rb
256
- - spec/chrono_model/adapter/sti_bug_spec.rb
257
- - spec/chrono_model/adapter_spec.rb
255
+ - spec/chrono_model/adapter/base_spec.rb
256
+ - spec/chrono_model/adapter/ddl_spec.rb
257
+ - spec/chrono_model/adapter/indexes_spec.rb
258
+ - spec/chrono_model/adapter/migrations_spec.rb
258
259
  - spec/chrono_model/conversions_spec.rb
260
+ - spec/chrono_model/history_models_spec.rb
259
261
  - spec/chrono_model/json_ops_spec.rb
260
- - spec/chrono_model/time_machine_spec.rb
261
- - spec/chrono_model/time_query_spec.rb
262
+ - spec/chrono_model/time_machine/as_of_spec.rb
263
+ - spec/chrono_model/time_machine/changes_spec.rb
264
+ - spec/chrono_model/time_machine/counter_cache_race_spec.rb
265
+ - spec/chrono_model/time_machine/default_scope_spec.rb
266
+ - spec/chrono_model/time_machine/history_spec.rb
267
+ - spec/chrono_model/time_machine/keep_cool_spec.rb
268
+ - spec/chrono_model/time_machine/manipulations_spec.rb
269
+ - spec/chrono_model/time_machine/model_identification_spec.rb
270
+ - spec/chrono_model/time_machine/sequence_spec.rb
271
+ - spec/chrono_model/time_machine/sti_spec.rb
272
+ - spec/chrono_model/time_machine/time_query_spec.rb
273
+ - spec/chrono_model/time_machine/timeline_spec.rb
274
+ - spec/chrono_model/time_machine/timestamps_spec.rb
275
+ - spec/chrono_model/time_machine/transactions_spec.rb
262
276
  - spec/config.travis.yml
263
277
  - spec/config.yml.example
264
278
  - spec/spec_helper.rb
279
+ - spec/support/adapter/helpers.rb
280
+ - spec/support/adapter/structure.rb
265
281
  - spec/support/aruba.rb
266
282
  - spec/support/connection.rb
267
- - spec/support/helpers.rb
268
283
  - spec/support/matchers/base.rb
269
284
  - spec/support/matchers/column.rb
270
285
  - spec/support/matchers/function.rb
271
286
  - spec/support/matchers/index.rb
272
287
  - spec/support/matchers/schema.rb
273
288
  - spec/support/matchers/table.rb
289
+ - spec/support/time_machine/helpers.rb
290
+ - spec/support/time_machine/structure.rb
274
291
  - sql/json_ops.sql
275
292
  - sql/uninstall-json_ops.sql
276
293
  homepage: https://github.com/ifad/chronomodel
@@ -308,22 +325,39 @@ test_files:
308
325
  - spec/aruba/fixtures/railsapp/config/environments/development.rb
309
326
  - spec/aruba/migrations_spec.rb
310
327
  - spec/aruba/rake_task_spec.rb
311
- - spec/chrono_model/adapter/counter_cache_race_spec.rb
312
- - spec/chrono_model/adapter/sti_bug_spec.rb
313
- - spec/chrono_model/adapter_spec.rb
328
+ - spec/chrono_model/adapter/base_spec.rb
329
+ - spec/chrono_model/adapter/ddl_spec.rb
330
+ - spec/chrono_model/adapter/indexes_spec.rb
331
+ - spec/chrono_model/adapter/migrations_spec.rb
314
332
  - spec/chrono_model/conversions_spec.rb
333
+ - spec/chrono_model/history_models_spec.rb
315
334
  - spec/chrono_model/json_ops_spec.rb
316
- - spec/chrono_model/time_machine_spec.rb
317
- - spec/chrono_model/time_query_spec.rb
335
+ - spec/chrono_model/time_machine/as_of_spec.rb
336
+ - spec/chrono_model/time_machine/changes_spec.rb
337
+ - spec/chrono_model/time_machine/counter_cache_race_spec.rb
338
+ - spec/chrono_model/time_machine/default_scope_spec.rb
339
+ - spec/chrono_model/time_machine/history_spec.rb
340
+ - spec/chrono_model/time_machine/keep_cool_spec.rb
341
+ - spec/chrono_model/time_machine/manipulations_spec.rb
342
+ - spec/chrono_model/time_machine/model_identification_spec.rb
343
+ - spec/chrono_model/time_machine/sequence_spec.rb
344
+ - spec/chrono_model/time_machine/sti_spec.rb
345
+ - spec/chrono_model/time_machine/time_query_spec.rb
346
+ - spec/chrono_model/time_machine/timeline_spec.rb
347
+ - spec/chrono_model/time_machine/timestamps_spec.rb
348
+ - spec/chrono_model/time_machine/transactions_spec.rb
318
349
  - spec/config.travis.yml
319
350
  - spec/config.yml.example
320
351
  - spec/spec_helper.rb
352
+ - spec/support/adapter/helpers.rb
353
+ - spec/support/adapter/structure.rb
321
354
  - spec/support/aruba.rb
322
355
  - spec/support/connection.rb
323
- - spec/support/helpers.rb
324
356
  - spec/support/matchers/base.rb
325
357
  - spec/support/matchers/column.rb
326
358
  - spec/support/matchers/function.rb
327
359
  - spec/support/matchers/index.rb
328
360
  - spec/support/matchers/schema.rb
329
361
  - spec/support/matchers/table.rb
362
+ - spec/support/time_machine/helpers.rb
363
+ - spec/support/time_machine/structure.rb
@@ -1,49 +0,0 @@
1
- require 'spec_helper'
2
- require 'support/helpers'
3
-
4
- describe 'models with STI' do
5
- include ChronoTest::Helpers::TimeMachine
6
-
7
- adapter.create_table 'animals', temporal: true do |t|
8
- t.string :type
9
- end
10
-
11
- class ::Animal < ActiveRecord::Base
12
- include ChronoModel::TimeMachine
13
- end
14
-
15
- class ::Dog < Animal
16
- end
17
-
18
- class ::Goat < Animal
19
- end
20
-
21
- describe 'it generates the right queries' do
22
- before do
23
- Dog.create!
24
- @later = Time.new
25
- Goat.create!
26
- end
27
-
28
- after do
29
- tables = ['temporal.animals', 'history.animals']
30
- ActiveRecord::Base.connection.execute "truncate #{tables.join(', ')} cascade"
31
- end
32
-
33
- specify "select" do
34
- expect(Animal.first).to_not be_nil
35
- expect(Animal.as_of(@later).first).to_not be_nil
36
- end
37
-
38
- specify "count" do
39
- expect(Animal.count).to eq(2)
40
- expect(Animal.as_of(@later).count).to eq(1)
41
-
42
- expect(Dog.count).to eq(1)
43
- expect(Dog.as_of(@later).count).to eq(1)
44
-
45
- expect(Goat.count).to eq(1)
46
- expect(Goat.as_of(@later).count).to eq(0)
47
- end
48
- end
49
- end
@@ -1,788 +0,0 @@
1
- require 'spec_helper'
2
- require 'support/helpers'
3
-
4
- shared_examples_for 'temporal table' do
5
- it { expect(adapter.is_chrono?(subject)).to be(true) }
6
-
7
- it { is_expected.to_not have_public_backing }
8
-
9
- it { is_expected.to have_temporal_backing }
10
- it { is_expected.to have_history_backing }
11
- it { is_expected.to have_history_extra_columns }
12
- it { is_expected.to have_history_functions }
13
- it { is_expected.to have_public_interface }
14
-
15
- it { is_expected.to have_columns(columns) }
16
- it { is_expected.to have_temporal_columns(columns) }
17
- it { is_expected.to have_history_columns(columns) }
18
- end
19
-
20
- shared_examples_for 'plain table' do
21
- it { expect(adapter.is_chrono?(subject)).to be(false) }
22
-
23
- it { is_expected.to have_public_backing }
24
-
25
- it { is_expected.to_not have_temporal_backing }
26
- it { is_expected.to_not have_history_backing }
27
- it { is_expected.to_not have_history_functions }
28
- it { is_expected.to_not have_public_interface }
29
-
30
- it { is_expected.to have_columns(columns) }
31
- end
32
-
33
- describe ChronoModel::Adapter do
34
- include ChronoTest::Helpers::Adapter
35
-
36
- context do
37
- subject { adapter }
38
- it { is_expected.to be_a_kind_of(ChronoModel::Adapter) }
39
-
40
- context do
41
- subject { adapter.adapter_name }
42
- it { is_expected.to eq 'PostgreSQL' }
43
- end
44
-
45
- context do
46
- before { expect(adapter).to receive(:postgresql_version).and_return(90300) }
47
- it { is_expected.to be_chrono_supported }
48
- end
49
-
50
- context do
51
- before { expect(adapter).to receive(:postgresql_version).and_return(90000) }
52
- it { is_expected.to_not be_chrono_supported }
53
- end
54
- end
55
-
56
- table 'test_table'
57
- subject { table }
58
-
59
- columns do
60
- native = [
61
- ['test', 'character varying'],
62
- ['foo', 'integer'],
63
- ['bar', 'double precision'],
64
- ['baz', 'text']
65
- ]
66
-
67
- def native.to_proc
68
- proc {|t|
69
- t.string :test, :null => false, :default => 'default-value'
70
- t.integer :foo
71
- t.float :bar
72
- t.text :baz
73
- t.integer :ary, :array => true, :null => false, :default => []
74
- t.boolean :bool, :null => false, :default => false
75
- }
76
- end
77
-
78
- native
79
- end
80
-
81
- describe '.is_chrono?' do
82
- with_temporal_table do
83
- it { expect(adapter.is_chrono?(table)).to be(true) }
84
- end
85
-
86
- with_plain_table do
87
- it { expect(adapter.is_chrono?(table)).to be(false) }
88
- end
89
-
90
- context 'when schemas are not there yet' do
91
- before(:all) do
92
- adapter.execute 'BEGIN'
93
- adapter.execute 'DROP SCHEMA temporal CASCADE'
94
- adapter.execute 'DROP SCHEMA history CASCADE'
95
- adapter.execute 'CREATE TABLE test_table (id integer)'
96
- end
97
-
98
- after(:all) do
99
- adapter.execute 'ROLLBACK'
100
- end
101
-
102
- it { expect { adapter.is_chrono?(table) }.to_not raise_error }
103
-
104
- it { expect(adapter.is_chrono?(table)).to be(false) }
105
- end
106
- end
107
-
108
- describe '.create_table' do
109
- with_temporal_table do
110
- it_should_behave_like 'temporal table'
111
- end
112
-
113
- with_plain_table do
114
- it_should_behave_like 'plain table'
115
- end
116
- end
117
-
118
- describe '.rename_table' do
119
- renamed = 'foo_table'
120
- subject { renamed }
121
-
122
- context ':temporal => true' do
123
- before :all do
124
- adapter.create_table table, :temporal => true, &columns
125
- adapter.add_index table, :test
126
- adapter.add_index table, [:foo, :bar]
127
-
128
- adapter.rename_table table, renamed
129
- end
130
- after(:all) { adapter.drop_table(renamed) }
131
-
132
- it_should_behave_like 'temporal table'
133
-
134
- it 'renames indexes' do
135
- new_index_names = adapter.indexes(renamed).map(&:name)
136
- expected_index_names = [[:test], [:foo, :bar]].map do |idx_cols|
137
- "index_#{renamed}_on_#{idx_cols.join('_and_')}"
138
- end
139
- expect(new_index_names.to_set).to eq expected_index_names.to_set
140
- end
141
- end
142
-
143
- context ':temporal => false' do
144
- before :all do
145
- adapter.create_table table, :temporal => false, &columns
146
-
147
- adapter.rename_table table, renamed
148
- end
149
- after(:all) { adapter.drop_table(renamed) }
150
-
151
- it_should_behave_like 'plain table'
152
- end
153
- end
154
-
155
- describe '.change_table' do
156
- with_temporal_table do
157
- before :all do
158
- adapter.change_table table, :temporal => false
159
- end
160
-
161
- it_should_behave_like 'plain table'
162
- end
163
-
164
- with_plain_table do
165
- before :all do
166
- adapter.add_index table, :foo
167
- adapter.add_index table, :bar, :unique => true
168
-
169
- adapter.change_table table, :temporal => true
170
- end
171
-
172
- it_should_behave_like 'temporal table'
173
-
174
- let(:history_indexes) do
175
- adapter.on_schema(ChronoModel::Adapter::HISTORY_SCHEMA) do
176
- adapter.indexes(table)
177
- end
178
- end
179
-
180
- it "copies plain index to history" do
181
- expect(history_indexes.find {|i| i.columns == ['foo']}).to be_present
182
- end
183
-
184
- it "copies unique index to history without uniqueness constraint" do
185
- expect(history_indexes.find {|i| i.columns == ['bar'] && i.unique == false}).to be_present
186
- end
187
- end
188
- end
189
-
190
- describe '.drop_table' do
191
- before :all do
192
- adapter.create_table table, :temporal => true, &columns
193
-
194
- adapter.drop_table table
195
- end
196
-
197
- it { is_expected.to_not have_public_backing }
198
- it { is_expected.to_not have_temporal_backing }
199
- it { is_expected.to_not have_history_backing }
200
- it { is_expected.to_not have_history_functions }
201
- it { is_expected.to_not have_public_interface }
202
- end
203
-
204
- describe '.add_index' do
205
- with_temporal_table do
206
- before :all do
207
- adapter.add_index table, [:foo, :bar], :name => 'foobar_index'
208
- adapter.add_index table, [:test], :name => 'test_index'
209
- end
210
-
211
- it { is_expected.to have_temporal_index 'foobar_index', %w( foo bar ) }
212
- it { is_expected.to have_history_index 'foobar_index', %w( foo bar ) }
213
- it { is_expected.to have_temporal_index 'test_index', %w( test ) }
214
- it { is_expected.to have_history_index 'test_index', %w( test ) }
215
-
216
- it { is_expected.to_not have_index 'foobar_index', %w( foo bar ) }
217
- it { is_expected.to_not have_index 'test_index', %w( test ) }
218
- end
219
-
220
- with_plain_table do
221
- before :all do
222
- adapter.add_index table, [:foo, :bar], :name => 'foobar_index'
223
- adapter.add_index table, [:test], :name => 'test_index'
224
- end
225
-
226
- it { is_expected.to_not have_temporal_index 'foobar_index', %w( foo bar ) }
227
- it { is_expected.to_not have_history_index 'foobar_index', %w( foo bar ) }
228
- it { is_expected.to_not have_temporal_index 'test_index', %w( test ) }
229
- it { is_expected.to_not have_history_index 'test_index', %w( test ) }
230
-
231
- it { is_expected.to have_index 'foobar_index', %w( foo bar ) }
232
- it { is_expected.to have_index 'test_index', %w( test ) }
233
- end
234
- end
235
-
236
- describe '.remove_index' do
237
- with_temporal_table do
238
- before :all do
239
- adapter.add_index table, [:foo, :bar], :name => 'foobar_index'
240
- adapter.add_index table, [:test], :name => 'test_index'
241
-
242
- adapter.remove_index table, :name => 'test_index'
243
- end
244
-
245
- it { is_expected.to_not have_temporal_index 'test_index', %w( test ) }
246
- it { is_expected.to_not have_history_index 'test_index', %w( test ) }
247
- it { is_expected.to_not have_index 'test_index', %w( test ) }
248
- end
249
-
250
- with_plain_table do
251
- before :all do
252
- adapter.add_index table, [:foo, :bar], :name => 'foobar_index'
253
- adapter.add_index table, [:test], :name => 'test_index'
254
-
255
- adapter.remove_index table, :name => 'test_index'
256
- end
257
-
258
- it { is_expected.to_not have_temporal_index 'test_index', %w( test ) }
259
- it { is_expected.to_not have_history_index 'test_index', %w( test ) }
260
- it { is_expected.to_not have_index 'test_index', %w( test ) }
261
- end
262
- end
263
-
264
- describe '.add_column' do
265
- let(:extra_columns) { [['foobarbaz', 'integer']] }
266
-
267
- with_temporal_table do
268
- before :all do
269
- adapter.add_column table, :foobarbaz, :integer
270
- end
271
-
272
- it { is_expected.to have_columns(extra_columns) }
273
- it { is_expected.to have_temporal_columns(extra_columns) }
274
- it { is_expected.to have_history_columns(extra_columns) }
275
- end
276
-
277
- with_plain_table do
278
- before :all do
279
- adapter.add_column table, :foobarbaz, :integer
280
- end
281
-
282
- it { is_expected.to have_columns(extra_columns) }
283
- end
284
- end
285
-
286
- describe '.remove_column' do
287
- let(:resulting_columns) { columns.reject {|c,_| c == 'foo'} }
288
-
289
- with_temporal_table do
290
- before :all do
291
- adapter.remove_column table, :foo
292
- end
293
-
294
- it { is_expected.to have_columns(resulting_columns) }
295
- it { is_expected.to have_temporal_columns(resulting_columns) }
296
- it { is_expected.to have_history_columns(resulting_columns) }
297
-
298
- it { is_expected.to_not have_columns([['foo', 'integer']]) }
299
- it { is_expected.to_not have_temporal_columns([['foo', 'integer']]) }
300
- it { is_expected.to_not have_history_columns([['foo', 'integer']]) }
301
- end
302
-
303
- with_plain_table do
304
- before :all do
305
- adapter.remove_column table, :foo
306
- end
307
-
308
- it { is_expected.to have_columns(resulting_columns) }
309
- it { is_expected.to_not have_columns([['foo', 'integer']]) }
310
- end
311
- end
312
-
313
- describe '.rename_column' do
314
- with_temporal_table do
315
- before :all do
316
- adapter.rename_column table, :foo, :taratapiatapioca
317
- end
318
-
319
- it { is_expected.to_not have_columns([['foo', 'integer']]) }
320
- it { is_expected.to_not have_temporal_columns([['foo', 'integer']]) }
321
- it { is_expected.to_not have_history_columns([['foo', 'integer']]) }
322
-
323
- it { is_expected.to have_columns([['taratapiatapioca', 'integer']]) }
324
- it { is_expected.to have_temporal_columns([['taratapiatapioca', 'integer']]) }
325
- it { is_expected.to have_history_columns([['taratapiatapioca', 'integer']]) }
326
- end
327
-
328
- with_plain_table do
329
- before :all do
330
- adapter.rename_column table, :foo, :taratapiatapioca
331
- end
332
-
333
- it { is_expected.to_not have_columns([['foo', 'integer']]) }
334
- it { is_expected.to have_columns([['taratapiatapioca', 'integer']]) }
335
- end
336
- end
337
-
338
- describe '.change_column' do
339
- with_temporal_table do
340
- before :all do
341
- adapter.change_column table, :foo, :float
342
- end
343
-
344
- it { is_expected.to_not have_columns([['foo', 'integer']]) }
345
- it { is_expected.to_not have_temporal_columns([['foo', 'integer']]) }
346
- it { is_expected.to_not have_history_columns([['foo', 'integer']]) }
347
-
348
- it { is_expected.to have_columns([['foo', 'double precision']]) }
349
- it { is_expected.to have_temporal_columns([['foo', 'double precision']]) }
350
- it { is_expected.to have_history_columns([['foo', 'double precision']]) }
351
- end
352
-
353
- with_plain_table do
354
- before(:all) do
355
- adapter.change_column table, :foo, :float
356
- end
357
-
358
- it { is_expected.to_not have_columns([['foo', 'integer']]) }
359
- it { is_expected.to have_columns([['foo', 'double precision']]) }
360
- end
361
- end
362
-
363
- describe '.remove_column' do
364
- with_temporal_table do
365
- before :all do
366
- adapter.remove_column table, :foo
367
- end
368
-
369
- it { is_expected.to_not have_columns([['foo', 'integer']]) }
370
- it { is_expected.to_not have_temporal_columns([['foo', 'integer']]) }
371
- it { is_expected.to_not have_history_columns([['foo', 'integer']]) }
372
- end
373
-
374
- with_plain_table do
375
- before :all do
376
- adapter.remove_column table, :foo
377
- end
378
-
379
- it { is_expected.to_not have_columns([['foo', 'integer']]) }
380
- end
381
- end
382
-
383
- describe '.column_definitions' do
384
- subject { adapter.column_definitions(table).map {|d| d.take(2)} }
385
-
386
- assert = proc do
387
- it { expect(subject & columns).to eq columns }
388
- it { is_expected.to include(['id', pk_type]) }
389
- end
390
-
391
- with_temporal_table(&assert)
392
- with_plain_table( &assert)
393
- end
394
-
395
- describe '.primary_key' do
396
- subject { adapter.primary_key(table) }
397
-
398
- assert = proc do
399
- it { is_expected.to eq 'id' }
400
- end
401
-
402
- with_temporal_table(&assert)
403
- with_plain_table( &assert)
404
- end
405
-
406
- describe '.indexes' do
407
- subject { adapter.indexes(table) }
408
-
409
- assert = proc do
410
- before(:all) do
411
- adapter.add_index table, :foo, :name => 'foo_index'
412
- adapter.add_index table, [:bar, :baz], :name => 'bar_index'
413
- end
414
-
415
- it { expect(subject.map(&:name)).to match_array %w( foo_index bar_index ) }
416
- it { expect(subject.map(&:columns)).to match_array [['foo'], ['bar', 'baz']] }
417
- end
418
-
419
- with_temporal_table(&assert)
420
- with_plain_table( &assert)
421
- end
422
-
423
- describe '.on_schema' do
424
- before(:all) do
425
- adapter.execute 'BEGIN'
426
- 5.times {|i| adapter.execute "CREATE SCHEMA test_#{i}"}
427
- end
428
-
429
- after(:all) do
430
- adapter.execute 'ROLLBACK'
431
- end
432
-
433
- context 'by default' do
434
- it 'saves the schema at each recursion' do
435
- is_expected.to be_in_schema(:default)
436
-
437
- adapter.on_schema('test_1') { is_expected.to be_in_schema('test_1')
438
- adapter.on_schema('test_2') { is_expected.to be_in_schema('test_2')
439
- adapter.on_schema('test_3') { is_expected.to be_in_schema('test_3')
440
- }
441
- is_expected.to be_in_schema('test_2')
442
- }
443
- is_expected.to be_in_schema('test_1')
444
- }
445
-
446
- is_expected.to be_in_schema(:default)
447
- end
448
-
449
- context 'when errors occur' do
450
- subject do
451
- adapter.on_schema('test_1') do
452
-
453
- adapter.on_schema('test_2') do
454
- adapter.execute 'BEGIN'
455
- adapter.execute 'ERRORING ON PURPOSE'
456
- end
457
-
458
- end
459
- end
460
-
461
- it {
462
- expect { subject }.
463
- to raise_error(/current transaction is aborted/).
464
- and change { adapter.instance_variable_get(:@schema_search_path) }
465
- }
466
-
467
- after do
468
- adapter.execute 'ROLLBACK'
469
- end
470
- end
471
- end
472
-
473
- context 'with recurse: :ignore' do
474
- it 'ignores recursive calls' do
475
- is_expected.to be_in_schema(:default)
476
-
477
- adapter.on_schema('test_1', recurse: :ignore) { is_expected.to be_in_schema('test_1')
478
- adapter.on_schema('test_2', recurse: :ignore) { is_expected.to be_in_schema('test_1')
479
- adapter.on_schema('test_3', recurse: :ignore) { is_expected.to be_in_schema('test_1')
480
- } } }
481
-
482
- is_expected.to be_in_schema(:default)
483
- end
484
- end
485
- end
486
-
487
- context 'migration extensions' do
488
- before :all do
489
- adapter.create_table :meetings do |t|
490
- t.string :name
491
- t.tsrange :interval
492
- end
493
- end
494
-
495
- after :all do
496
- adapter.drop_table :meetings
497
- end
498
-
499
- describe '.add_temporal_indexes' do
500
- before do
501
- adapter.add_temporal_indexes :meetings, :interval
502
- end
503
-
504
- it { expect(adapter.indexes(:meetings).map(&:name)).to eq [
505
- 'index_meetings_temporal_on_interval',
506
- 'index_meetings_temporal_on_lower_interval',
507
- 'index_meetings_temporal_on_upper_interval'
508
- ] }
509
-
510
- after do
511
- adapter.remove_temporal_indexes :meetings, :interval
512
- end
513
- end
514
-
515
- describe '.remove_temporal_indexes' do
516
- before :all do
517
- adapter.add_temporal_indexes :meetings, :interval
518
- end
519
-
520
- before do
521
- adapter.remove_temporal_indexes :meetings, :interval
522
- end
523
-
524
- it { expect(adapter.indexes(:meetings)).to be_empty }
525
- end
526
-
527
- describe '.add_timeline_consistency_constraint' do
528
- before do
529
- adapter.add_timeline_consistency_constraint(:meetings, :interval)
530
- end
531
-
532
- it { expect(adapter.indexes(:meetings).map(&:name)).to eq [
533
- 'meetings_timeline_consistency'
534
- ] }
535
-
536
- after do
537
- adapter.remove_timeline_consistency_constraint(:meetings)
538
- end
539
- end
540
-
541
- describe '.remove_timeline_consistency_constraint' do
542
- before :all do
543
- adapter.add_timeline_consistency_constraint :meetings, :interval
544
- end
545
-
546
- before do
547
- adapter.remove_timeline_consistency_constraint(:meetings)
548
- end
549
-
550
- it { expect(adapter.indexes(:meetings)).to be_empty }
551
- end
552
- end
553
-
554
- let(:current) { [ChronoModel::Adapter::TEMPORAL_SCHEMA, table].join('.') }
555
- let(:history) { [ChronoModel::Adapter::HISTORY_SCHEMA, table].join('.') }
556
-
557
- def count(table)
558
- adapter.select_value("SELECT COUNT(*) FROM ONLY #{table}").to_i
559
- end
560
-
561
- def ids(table)
562
- adapter.select_values("SELECT id FROM ONLY #{table} ORDER BY id")
563
- end
564
-
565
- context 'INSERT multiple values' do
566
- before :all do
567
- adapter.create_table table, :temporal => true, &columns
568
- end
569
-
570
- after :all do
571
- adapter.drop_table table
572
- end
573
-
574
- context 'when succeeding' do
575
- def insert
576
- adapter.execute <<-SQL
577
- INSERT INTO #{table} (test, foo) VALUES
578
- ('test1', 1),
579
- ('test2', 2);
580
- SQL
581
- end
582
-
583
- it { expect { insert }.to_not raise_error }
584
- it { expect(count(current)).to eq 2 }
585
- it { expect(count(history)).to eq 2 }
586
- end
587
-
588
- context 'when failing' do
589
- def insert
590
- adapter.execute <<-SQL
591
- INSERT INTO #{table} (test, foo) VALUES
592
- ('test3', 3),
593
- (NULL, 0);
594
- SQL
595
- end
596
-
597
- it { expect { insert }.to raise_error(ActiveRecord::StatementInvalid) }
598
- it { expect(count(current)).to eq 2 } # Because the previous
599
- it { expect(count(history)).to eq 2 } # records are preserved
600
- end
601
-
602
- context 'after a failure' do
603
- def insert
604
- adapter.execute <<-SQL
605
- INSERT INTO #{table} (test, foo) VALUES
606
- ('test4', 3),
607
- ('test5', 4);
608
- SQL
609
- end
610
-
611
- it { expect { insert }.to_not raise_error }
612
-
613
- it { expect(count(current)).to eq 4 }
614
- it { expect(count(history)).to eq 4 }
615
-
616
- it { expect(ids(current)).to eq ids(history) }
617
- end
618
- end
619
-
620
- context 'INSERT on NOT NULL columns but with a DEFAULT value' do
621
- before :all do
622
- adapter.create_table table, :temporal => true, &columns
623
- end
624
-
625
- after :all do
626
- adapter.drop_table table
627
- end
628
-
629
- def insert
630
- adapter.execute <<-SQL
631
- INSERT INTO #{table} DEFAULT VALUES
632
- SQL
633
- end
634
-
635
- def select
636
- adapter.select_values <<-SQL
637
- SELECT test FROM #{table}
638
- SQL
639
- end
640
-
641
- it { expect { insert }.to_not raise_error }
642
- it { insert; expect(select.uniq).to eq ['default-value'] }
643
- end
644
-
645
- context 'redundant UPDATEs' do
646
-
647
- before :all do
648
- adapter.create_table table, :temporal => true, &columns
649
-
650
- adapter.execute <<-SQL
651
- INSERT INTO #{table} (test, foo) VALUES ('test1', 1);
652
- SQL
653
-
654
- adapter.execute <<-SQL
655
- UPDATE #{table} SET test = 'test2';
656
- SQL
657
-
658
- adapter.execute <<-SQL
659
- UPDATE #{table} SET test = 'test2';
660
- SQL
661
- end
662
-
663
- after :all do
664
- adapter.drop_table table
665
- end
666
-
667
- it { expect(count(current)).to eq 1 }
668
- it { expect(count(history)).to eq 2 }
669
-
670
- end
671
-
672
- context 'updates on non-journaled fields' do
673
- before :all do
674
- adapter.create_table table, :temporal => true do |t|
675
- t.string 'test'
676
- t.timestamps null: false
677
- end
678
-
679
- adapter.execute <<-SQL
680
- INSERT INTO #{table} (test, created_at, updated_at) VALUES ('test', now(), now());
681
- SQL
682
-
683
- adapter.execute <<-SQL
684
- UPDATE #{table} SET test = 'test2', updated_at = now();
685
- SQL
686
-
687
- 2.times do
688
- adapter.execute <<-SQL # Redundant update with only updated_at change
689
- UPDATE #{table} SET test = 'test2', updated_at = now();
690
- SQL
691
-
692
- adapter.execute <<-SQL
693
- UPDATE #{table} SET updated_at = now();
694
- SQL
695
- end
696
- end
697
-
698
- after :all do
699
- adapter.drop_table table
700
- end
701
-
702
- it { expect(count(current)).to eq 1 }
703
- it { expect(count(history)).to eq 2 }
704
- end
705
-
706
- context 'selective journaled fields' do
707
- describe 'basic behaviour' do
708
- specify do
709
- adapter.create_table table, :temporal => true, :journal => %w( foo ) do |t|
710
- t.string 'foo'
711
- t.string 'bar'
712
- end
713
-
714
- adapter.execute <<-SQL
715
- INSERT INTO #{table} (foo, bar) VALUES ('test foo', 'test bar');
716
- SQL
717
-
718
- adapter.execute <<-SQL
719
- UPDATE #{table} SET foo = 'test foo', bar = 'no history';
720
- SQL
721
-
722
- 2.times do
723
- adapter.execute <<-SQL
724
- UPDATE #{table} SET bar = 'really no history';
725
- SQL
726
- end
727
-
728
- expect(count(current)).to eq 1
729
- expect(count(history)).to eq 1
730
-
731
- adapter.drop_table table
732
- end
733
- end
734
-
735
- describe 'schema changes' do
736
- table 'journaled_things'
737
-
738
- before do
739
- adapter.create_table table, :temporal => true, :journal => %w( foo ) do |t|
740
- t.string 'foo'
741
- t.string 'bar'
742
- t.string 'baz'
743
- end
744
- end
745
-
746
- after do
747
- adapter.drop_table table
748
- end
749
-
750
- it 'preserves options upon column change' do
751
- adapter.change_table table, temporal: true, journal: %w(foo bar)
752
-
753
- adapter.execute <<-SQL
754
- INSERT INTO #{table} (foo, bar) VALUES ('test foo', 'test bar');
755
- SQL
756
-
757
- expect(count(current)).to eq 1
758
- expect(count(history)).to eq 1
759
-
760
- adapter.execute <<-SQL
761
- UPDATE #{table} SET foo = 'test foo', bar = 'chronomodel';
762
- SQL
763
-
764
- expect(count(current)).to eq 1
765
- expect(count(history)).to eq 2
766
- end
767
-
768
- it 'changes option upon table change' do
769
- adapter.change_table table, temporal: true, journal: %w(bar)
770
-
771
- adapter.execute <<-SQL
772
- INSERT INTO #{table} (foo, bar) VALUES ('test foo', 'test bar');
773
- UPDATE #{table} SET foo = 'test foo', bar = 'no history';
774
- SQL
775
-
776
- expect(count(current)).to eq 1
777
- expect(count(history)).to eq 1
778
-
779
- adapter.execute <<-SQL
780
- UPDATE #{table} SET foo = 'test foo again', bar = 'no history';
781
- SQL
782
-
783
- expect(count(current)).to eq 1
784
- expect(count(history)).to eq 1
785
- end
786
- end
787
- end
788
- end