sequel_bitemporal 0.6.11 → 0.6.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,7 +2,6 @@ require "spec_helper"
2
2
  require "json"
3
3
 
4
4
  describe "Sequel::Plugins::Bitemporal" do
5
- include DbHelpers
6
5
  before :all do
7
6
  db_setup
8
7
  @version_class.instance_eval do
@@ -2,7 +2,6 @@ require "spec_helper"
2
2
 
3
3
  describe "Sequel::Plugins::Bitemporal" do
4
4
  let(:hour){ 3600 }
5
- include DbHelpers
6
5
  before :all do
7
6
  db_setup use_time: true
8
7
  end
@@ -359,17 +358,17 @@ describe "Sequel::Plugins::Bitemporal" do
359
358
  Timecop.freeze Time.now+1*hour
360
359
  master.update_attributes name: "Single Standard", price: 99
361
360
  master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
362
- res = @master_class.eager_graph(:current_or_future_versions).where({current_or_future_versions__id: nil}.sql_negate & {price: 99}).all.first
361
+ res = @master_class.eager_graph(:current_or_future_versions).where(Sequel.negate(current_or_future_versions__id: nil) & {price: 99}).all.first
363
362
  res.should be
364
363
  res.current_or_future_versions.should have(1).item
365
364
  res.current_or_future_versions.first.price.should == 99
366
- res = @master_class.eager_graph(:current_or_future_versions).where({current_or_future_versions__id: nil}.sql_negate & {price: 94}).all.first
365
+ res = @master_class.eager_graph(:current_or_future_versions).where(Sequel.negate(current_or_future_versions__id: nil) & {price: 94}).all.first
367
366
  res.should be
368
367
  res.current_or_future_versions.should have(1).item
369
368
  res.current_or_future_versions.first.price.should == 94
370
369
  Timecop.freeze Time.now+1*hour
371
370
  master.destroy
372
- @master_class.eager_graph(:current_or_future_versions).where({current_or_future_versions__id: nil}.sql_negate).all.should be_empty
371
+ @master_class.eager_graph(:current_or_future_versions).where(Sequel.negate(current_or_future_versions__id: nil)).all.should be_empty
373
372
  end
374
373
  it "allows loading masters with current or future versions" do
375
374
  master_destroyed = @master_class.new
@@ -383,7 +382,6 @@ describe "Sequel::Plugins::Bitemporal" do
383
382
  end
384
383
  end
385
384
  describe "Sequel::Plugins::Bitemporal", "with audit" do
386
- include DbHelpers
387
385
  before :all do
388
386
  @audit_class = Class.new
389
387
  db_setup use_time: true, audit_class: @audit_class
@@ -438,7 +436,6 @@ describe "Sequel::Plugins::Bitemporal", "with audit" do
438
436
  end
439
437
 
440
438
  describe "Sequel::Plugins::Bitemporal", "with audit, specifying how to get the author" do
441
- include DbHelpers
442
439
  before :all do
443
440
  @audit_class = Class.new
444
441
  db_setup use_time: true, audit_class: @audit_class, audit_updated_by_method: :author
@@ -0,0 +1,496 @@
1
+ require "spec_helper"
2
+ if DbHelpers.pg?
3
+ describe "Sequel::Plugins::Bitemporal" do
4
+ let(:hour){ 3600 }
5
+ before :all do
6
+ db_setup use_time: true, ranges: true
7
+ end
8
+ before do
9
+ Timecop.freeze 2009, 11, 28, 10
10
+ end
11
+ after do
12
+ Timecop.return
13
+ end
14
+ it "checks version class is given" do
15
+ lambda{
16
+ @version_class.plugin :bitemporal
17
+ }.should raise_error Sequel::Error, "please specify version class to use for bitemporal plugin"
18
+ end
19
+ it "checks required columns are present" do
20
+ lambda{
21
+ @version_class.plugin :bitemporal, :version_class => @master_class
22
+ }.should raise_error Sequel::Error, "bitemporal plugin requires the following missing columns on version class: master_id, valid_from, valid_to, created_at, expired_at"
23
+ end
24
+ it "propagates errors from version to master" do
25
+ master = @master_class.new
26
+ master.should be_valid
27
+ master.attributes = {name: "Single Standard"}
28
+ master.should_not be_valid
29
+ master.errors.should == {price: ["is required"]}
30
+ end
31
+ it "#update_attributes returns false instead of raising errors" do
32
+ master = @master_class.new
33
+ master.update_attributes(name: "Single Standard").should be_false
34
+ master.should be_new
35
+ master.errors.should == {price: ["is required"]}
36
+ master.update_attributes(price: 98).should be_true
37
+ end
38
+ it "allows creating a master and its first version in one step" do
39
+ master = @master_class.new
40
+ master.update_attributes(name: "Single Standard", price: 98).should be_true
41
+ master.should_not be_new
42
+ master.should have_versions %Q{
43
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
44
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | MAX TIME | true |
45
+ }
46
+ end
47
+ it "allows creating a new version in the past" do
48
+ master = @master_class.new
49
+ master.update_attributes name: "Single Standard", price: 98, valid_from: Time.now-hour
50
+ master.should have_versions %Q{
51
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
52
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 09:00:00 +0000 | MAX TIME | true |
53
+ }
54
+ end
55
+ it "allows creating a new version in the future" do
56
+ master = @master_class.new
57
+ master.update_attributes name: "Single Standard", price: 98, valid_from: Time.now+hour
58
+ master.should have_versions %Q{
59
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
60
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | MAX TIME | |
61
+ }
62
+ end
63
+ it "doesn't loose previous version in same-day update" do
64
+ master = @master_class.new
65
+ master.update_attributes name: "Single Standard", price: 98
66
+ master.update_attributes name: "Single Standard", price: 94
67
+ master.should have_versions %Q{
68
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
69
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
70
+ | Single Standard | 94 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | MAX TIME | true |
71
+ }
72
+ end
73
+ it "allows partial updating based on current version" do
74
+ master = @master_class.new
75
+ master.update_attributes name: "Single Standard", price: 98
76
+ master.update_attributes price: 94
77
+ master.update_attributes name: "King Size"
78
+ master.should have_versions %Q{
79
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
80
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
81
+ | Single Standard | 94 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
82
+ | King Size | 94 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | MAX TIME | true |
83
+ }
84
+ end
85
+ it "expires previous version but keep it in history" do
86
+ master = @master_class.new
87
+ master.update_attributes name: "Single Standard", price: 98
88
+ Timecop.freeze Time.now+hour
89
+ master.update_attributes price: 94
90
+ master.should have_versions %Q{
91
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
92
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
93
+ | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
94
+ | Single Standard | 94 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | MAX TIME | true |
95
+ }
96
+ end
97
+ it "doesn't expire no longer valid versions" do
98
+ master = @master_class.new
99
+ master.update_attributes name: "Single Standard", price: 98, valid_to: Time.now+hour
100
+ Timecop.freeze Time.now+hour
101
+ master.update_attributes(price: 94).should be_false
102
+ master.update_attributes name: "Single Standard", price: 94
103
+ master.should have_versions %Q{
104
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
105
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
106
+ | Single Standard | 94 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | MAX TIME | true |
107
+ }
108
+ end
109
+ it "allows shortening validity (SEE COMMENTS FOR IMPROVEMENTS)" do
110
+ master = @master_class.new
111
+ master.update_attributes name: "Single Standard", price: 98
112
+ Timecop.freeze Time.now+hour
113
+ master.update_attributes valid_to: Time.now+10*hour
114
+ master.should have_versions %Q{
115
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
116
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
117
+ | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
118
+ | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | 2009-11-28 21:00:00 +0000 | true |
119
+ }
120
+ # would be even better if it could be:
121
+ # | name | price | created_at | expired_at | valid_from | valid_to | current |
122
+ # | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
123
+ # | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 21:00:00 +0000 | true |
124
+ end
125
+ it "allows extending validity (SEE COMMENTS FOR IMPROVEMENTS)" do
126
+ master = @master_class.new
127
+ master.update_attributes name: "Single Standard", price: 98, valid_to: Time.now+2*hour
128
+ Timecop.freeze Time.now+hour
129
+ master.should have_versions %Q{
130
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
131
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 12:00:00 +0000 | true |
132
+ }
133
+ master.update_attributes valid_to: nil
134
+ master.should have_versions %Q{
135
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
136
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 12:00:00 +0000 | |
137
+ | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
138
+ | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | MAX TIME | true |
139
+ }
140
+ # would be even better if it could be:
141
+ # | name | price | created_at | expired_at | valid_from | valid_to | current |
142
+ # | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 12:00:00 +0000 | |
143
+ # | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | MAX TIME | true |
144
+ end
145
+ it "don't create any new version without change " do
146
+ master = @master_class.new
147
+ master.update_attributes name: "Single Standard", price: 98
148
+ master.update_attributes price: 98
149
+ master.update_attributes name: "Single Standard", price: 98
150
+ master.should have_versions %Q{
151
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
152
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 10:00:00 +0000| MAX TIME | true |
153
+ }
154
+ end
155
+ it "change in validity still creates a new version (SEE COMMENTS FOR IMPROVEMENTS)" do
156
+ master = @master_class.new
157
+ master.update_attributes name: "Single Standard", price: 98
158
+ Timecop.freeze Time.now+hour
159
+ master.update_attributes price: 98, valid_from: Time.now-2*hour
160
+ master.update_attributes price: 98, valid_from: Time.now+1*hour
161
+ master.should have_versions %Q{
162
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
163
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | MAX TIME | true |
164
+ | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 09:00:00 +0000 | 2009-11-28 10:00:00 +0000 | |
165
+ }
166
+ # would be even better if it could be:
167
+ # | name | price | created_at | expired_at | valid_from | valid_to | current |
168
+ # | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
169
+ # | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 09:00:00 +0000 | MAX TIME | true |
170
+ end
171
+ it "overrides no future versions" do
172
+ master = @master_class.new
173
+ master.update_attributes name: "Single Standard", price: 98, valid_to: Time.now+2*hour
174
+ master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour, valid_to: Time.now+4*hour
175
+ master.update_attributes name: "Single Standard", price: 95, valid_from: Time.now+4*hour, valid_to: Time.now+6*hour
176
+ Timecop.freeze Time.now+hour
177
+ master.update_attributes name: "King Size", valid_to: nil
178
+ master.should have_versions %Q{
179
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
180
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 12:00:00 +0000 | |
181
+ | Single Standard | 94 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 12:00:00 +0000 | 2009-11-28 14:00:00 +0000 | |
182
+ | Single Standard | 95 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 14:00:00 +0000 | 2009-11-28 16:00:00 +0000 | |
183
+ | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
184
+ | King Size | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | 2009-11-28 12:00:00 +0000 | true |
185
+ }
186
+ end
187
+ it "overrides multiple future versions" do
188
+ master = @master_class.new
189
+ master.update_attributes name: "Single Standard", price: 98, valid_to: Time.now+2*hour
190
+ master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour, valid_to: Time.now+4*hour
191
+ master.update_attributes name: "Single Standard", price: 95, valid_from: Time.now+4*hour, valid_to: Time.now+6*hour
192
+ Timecop.freeze Time.now+hour
193
+ master.update_attributes name: "King Size", valid_to: Time.now+4*hour
194
+ master.should have_versions %Q{
195
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
196
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 12:00:00 +0000 | |
197
+ | Single Standard | 94 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 12:00:00 +0000 | 2009-11-28 14:00:00 +0000 | |
198
+ | Single Standard | 95 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 14:00:00 +0000 | 2009-11-28 16:00:00 +0000 | |
199
+ | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
200
+ | Single Standard | 95 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 15:00:00 +0000 | 2009-11-28 16:00:00 +0000 | |
201
+ | King Size | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | 2009-11-28 15:00:00 +0000 | true |
202
+ }
203
+ end
204
+ it "overrides all future versions" do
205
+ master = @master_class.new
206
+ master.update_attributes name: "Single Standard", price: 98, valid_to: Time.now+2*hour
207
+ master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour, valid_to: Time.now+4*hour
208
+ master.update_attributes name: "Single Standard", price: 95, valid_from: Time.now+4*hour, valid_to: Time.now+6*hour
209
+ Timecop.freeze Time.now+hour
210
+ master.update_attributes name: "King Size", valid_to: Time.utc(9999)
211
+ master.should have_versions %Q{
212
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
213
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 12:00:00 +0000 | |
214
+ | Single Standard | 94 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 12:00:00 +0000 | 2009-11-28 14:00:00 +0000 | |
215
+ | Single Standard | 95 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 14:00:00 +0000 | 2009-11-28 16:00:00 +0000 | |
216
+ | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
217
+ | King Size | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | MAX TIME | true |
218
+ }
219
+ end
220
+ it "allows deleting current version" do
221
+ master = @master_class.new
222
+ master.update_attributes name: "Single Standard", price: 98
223
+ master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
224
+ Timecop.freeze Time.now+hour
225
+ master.current_version.destroy.should be_true
226
+ master.should have_versions %Q{
227
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
228
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
229
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 12:00:00 +0000 | |
230
+ | Single Standard | 94 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 12:00:00 +0000 | MAX TIME | |
231
+ | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
232
+ }
233
+ end
234
+ it "allows deleting a future version" do
235
+ master = @master_class.new
236
+ master.update_attributes name: "Single Standard", price: 98
237
+ master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
238
+ Timecop.freeze Time.now+hour
239
+ master.versions.last.destroy.should be_true
240
+ master.should have_versions %Q{
241
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
242
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
243
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 12:00:00 +0000 | |
244
+ | Single Standard | 94 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 12:00:00 +0000 | MAX TIME | |
245
+ | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | MAX TIME | true |
246
+ }
247
+ end
248
+ it "allows deleting all versions" do
249
+ master = @master_class.new
250
+ master.update_attributes name: "Single Standard", price: 98
251
+ master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
252
+ Timecop.freeze Time.now+hour
253
+ master.destroy.should be_true
254
+ master.should have_versions %Q{
255
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
256
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
257
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 12:00:00 +0000 | |
258
+ | Single Standard | 94 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 12:00:00 +0000 | MAX TIME | |
259
+ }
260
+ end
261
+ it "allows simultaneous updates without information loss" do
262
+ master = @master_class.new
263
+ master.update_attributes name: "Single Standard", price: 98
264
+ Timecop.freeze Time.now+hour
265
+ master2 = @master_class.find id: master.id
266
+ master.update_attributes name: "Single Standard", price: 94
267
+ master2.update_attributes name: "Single Standard", price: 95
268
+ master.should have_versions %Q{
269
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
270
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
271
+ | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
272
+ | Single Standard | 94 | 2009-11-28 11:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 11:00:00 +0000 | MAX TIME | |
273
+ | Single Standard | 95 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | MAX TIME | true |
274
+ }
275
+ end
276
+ it "allows simultaneous cumulative updates" do
277
+ master = @master_class.new
278
+ master.update_attributes name: "Single Standard", price: 98
279
+ Timecop.freeze Time.now+hour
280
+ master2 = @master_class.find id: master.id
281
+ master.update_attributes price: 94
282
+ master2.update_attributes name: "King Size"
283
+ master.should have_versions %Q{
284
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
285
+ | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
286
+ | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
287
+ | Single Standard | 94 | 2009-11-28 11:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 11:00:00 +0000 | MAX TIME | |
288
+ | King Size | 94 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | MAX TIME | true |
289
+ }
290
+ end
291
+ it "allows eager loading with conditions on current version" do
292
+ master = @master_class.new
293
+ master.update_attributes name: "Single Standard", price: 98
294
+ master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
295
+ @master_class.eager_graph(:current_version).where("rooms_current_version.id IS NOT NULL").first.should be
296
+ Timecop.freeze Time.now+hour
297
+ master.destroy
298
+ @master_class.eager_graph(:current_version).where("rooms_current_version.id IS NOT NULL").first.should be_nil
299
+ end
300
+ it "allows loading masters with a current version" do
301
+ master_destroyed = @master_class.new
302
+ master_destroyed.update_attributes name: "Single Standard", price: 98
303
+ master_destroyed.destroy
304
+ master_with_current = @master_class.new
305
+ master_with_current.update_attributes name: "Single Standard", price: 94
306
+ master_with_future = @master_class.new
307
+ master_with_future.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
308
+ @master_class.with_current_version.all.should have(1).item
309
+ end
310
+ it "gets pending or current version attributes" do
311
+ master = @master_class.new
312
+ master.attributes.should == {}
313
+ master.pending_version.should be_nil
314
+ master.current_version.should be_nil
315
+ master.pending_or_current_version.name.should be_nil
316
+ master.name.should be_nil
317
+
318
+ master.update_attributes name: "Single Standard", price: 98
319
+ master.attributes[:name].should == "Single Standard"
320
+ master.pending_version.should be_nil
321
+ master.pending_or_current_version.name.should == "Single Standard"
322
+ master.name.should == "Single Standard"
323
+
324
+ master.attributes = {name: "King Size"}
325
+ master.attributes[:name].should == "King Size"
326
+ master.pending_version.should be
327
+ master.pending_or_current_version.name.should == "King Size"
328
+ master.name.should == "King Size"
329
+ end
330
+ it "allows to go back in time" do
331
+ master = @master_class.new
332
+ master.update_attributes name: "Single Standard", price: 98
333
+ Timecop.freeze Time.now+1*hour
334
+ master.update_attributes price: 94
335
+ master.current_version.price.should == 94
336
+ Sequel::Plugins::Bitemporal.as_we_knew_it(Time.now-1*hour) do
337
+ master.current_version(true).price.should == 98
338
+ end
339
+ end
340
+ it "correctly reset time if failure when going back in time" do
341
+ before = Sequel::Plugins::Bitemporal.now
342
+ lambda do
343
+ Sequel::Plugins::Bitemporal.at(Time.now+1*hour) do
344
+ raise StandardError, "error during back in time"
345
+ end
346
+ end.should raise_error StandardError
347
+ Sequel::Plugins::Bitemporal.now.should == before
348
+ lambda do
349
+ Sequel::Plugins::Bitemporal.as_we_knew_it(Time.now-1*hour) do
350
+ raise StandardError, "error during back in time"
351
+ end
352
+ end.should raise_error StandardError
353
+ Sequel::Plugins::Bitemporal.now.should == before
354
+ end
355
+ it "allows eager loading with conditions on current or future versions" do
356
+ master = @master_class.new
357
+ master.update_attributes name: "Single Standard", price: 98
358
+ Timecop.freeze Time.now+1*hour
359
+ master.update_attributes name: "Single Standard", price: 99
360
+ master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
361
+ res = @master_class.eager_graph(:current_or_future_versions).where(Sequel.negate(current_or_future_versions__id: nil) & {price: 99}).all.first
362
+ res.should be
363
+ res.current_or_future_versions.should have(1).item
364
+ res.current_or_future_versions.first.price.should == 99
365
+ res = @master_class.eager_graph(:current_or_future_versions).where(Sequel.negate(current_or_future_versions__id: nil) & {price: 94}).all.first
366
+ res.should be
367
+ res.current_or_future_versions.should have(1).item
368
+ res.current_or_future_versions.first.price.should == 94
369
+ Timecop.freeze Time.now+1*hour
370
+ master.destroy
371
+ @master_class.eager_graph(:current_or_future_versions).where(Sequel.negate(current_or_future_versions__id: nil)).all.should be_empty
372
+ end
373
+ it "allows loading masters with current or future versions" do
374
+ master_destroyed = @master_class.new
375
+ master_destroyed.update_attributes name: "Single Standard", price: 98
376
+ master_destroyed.destroy
377
+ master_with_current = @master_class.new
378
+ master_with_current.update_attributes name: "Single Standard", price: 94
379
+ master_with_future = @master_class.new
380
+ master_with_future.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
381
+ @master_class.with_current_or_future_versions.all.should have(2).item
382
+ end
383
+ end
384
+ describe "Sequel::Plugins::Bitemporal", "with audit" do
385
+ before :all do
386
+ @audit_class = Class.new
387
+ db_setup use_time: true, audit_class: @audit_class, ranges: true
388
+ end
389
+ before do
390
+ Timecop.freeze 2009, 11, 28, 10
391
+ end
392
+ after do
393
+ Timecop.return
394
+ end
395
+ let(:author){ mock :author, audit_kind: "user" }
396
+ it "generates a new audit on creation" do
397
+ master = @master_class.new
398
+ master.should_receive(:updated_by).and_return author
399
+ @audit_class.should_receive(:audit).with(
400
+ master,
401
+ {},
402
+ hash_including({name: "Single Standard", price: 98}),
403
+ Time.now,
404
+ author
405
+ )
406
+ master.update_attributes name: "Single Standard", price: 98
407
+ end
408
+ it "generates a new audit on full update" do
409
+ master = @master_class.new
410
+ master.should_receive(:updated_by).twice.and_return author
411
+ @audit_class.stub(:audit)
412
+ master.update_attributes name: "Single Standard", price: 98
413
+ @audit_class.should_receive(:audit).with(
414
+ master,
415
+ hash_including({name: "Single Standard", price: 98}),
416
+ hash_including({name: "King size", price: 98}),
417
+ Time.now,
418
+ author
419
+ )
420
+ master.update_attributes name: "King size", price: 98
421
+ end
422
+ it "generates a new audit on partial update" do
423
+ master = @master_class.new
424
+ master.should_receive(:updated_by).twice.and_return author
425
+ @audit_class.stub(:audit)
426
+ master.update_attributes name: "Single Standard", price: 98
427
+ @audit_class.should_receive(:audit).with(
428
+ master,
429
+ hash_including({name: "Single Standard", price: 98}),
430
+ hash_including({name: "King size", price: 98}),
431
+ Time.now,
432
+ author
433
+ )
434
+ master.update_attributes name: "King size", price: 98
435
+ end
436
+ end
437
+
438
+ describe "Sequel::Plugins::Bitemporal", "with audit, specifying how to get the author" do
439
+ before :all do
440
+ @audit_class = Class.new
441
+ db_setup(
442
+ use_time: true,
443
+ audit_class: @audit_class,
444
+ audit_updated_by_method: :author,
445
+ ranges: true
446
+ )
447
+ end
448
+ before do
449
+ Timecop.freeze 2009, 11, 28, 10
450
+ end
451
+ after do
452
+ Timecop.return
453
+ end
454
+ let(:author){ mock :author, audit_kind: "user" }
455
+ it "generates a new audit on creation" do
456
+ master = @master_class.new
457
+ master.should_receive(:author).and_return author
458
+ @audit_class.should_receive(:audit).with(
459
+ master,
460
+ {},
461
+ hash_including({name: "Single Standard", price: 98}),
462
+ Time.now,
463
+ author
464
+ )
465
+ master.update_attributes name: "Single Standard", price: 98
466
+ end
467
+ it "generates a new audit on full update" do
468
+ master = @master_class.new
469
+ master.should_receive(:author).twice.and_return author
470
+ @audit_class.stub(:audit)
471
+ master.update_attributes name: "Single Standard", price: 98
472
+ @audit_class.should_receive(:audit).with(
473
+ master,
474
+ hash_including({name: "Single Standard", price: 98}),
475
+ hash_including({name: "King size", price: 98}),
476
+ Time.now,
477
+ author
478
+ )
479
+ master.update_attributes name: "King size", price: 98
480
+ end
481
+ it "generates a new audit on partial update" do
482
+ master = @master_class.new
483
+ master.should_receive(:author).twice.and_return author
484
+ @audit_class.stub(:audit)
485
+ master.update_attributes name: "Single Standard", price: 98
486
+ @audit_class.should_receive(:audit).with(
487
+ master,
488
+ hash_including({name: "Single Standard", price: 98}),
489
+ hash_including({name: "King size", price: 98}),
490
+ Time.now,
491
+ author
492
+ )
493
+ master.update_attributes name: "King size", price: 98
494
+ end
495
+ end
496
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,27 @@
1
1
  require "sequel"
2
2
  require "timecop"
3
3
  require "pry"
4
- DB = Sequel.sqlite
4
+
5
5
  Dir[File.expand_path("../support/*.rb", __FILE__)].each{|f| require f}
6
6
  ENV["TZ"]="UTC"
7
+
8
+ DB = if DbHelpers.pg?
9
+ `createdb sequel_bitemporal_test`
10
+ Sequel.extension :pg_range, :pg_range_ops
11
+ Sequel.postgres "sequel_bitemporal_test"
12
+ else
13
+ Sequel.sqlite
14
+ end
15
+
16
+ if ENV["DEBUG"]
17
+ require "logger"
18
+ DB.loggers << Logger.new($stdout)
19
+ end
20
+
21
+ RSpec.configure do |config|
22
+ config.include DbHelpers
23
+
24
+ config.before :each do
25
+ db_truncate
26
+ end
27
+ end
data/spec/support/db.rb CHANGED
@@ -1,13 +1,11 @@
1
1
  module DbHelpers
2
- def self.included(klass)
3
- klass.before do
4
- db_truncate
5
- end
2
+
3
+ def self.pg?
4
+ ENV.has_key? "PG"
6
5
  end
7
6
 
8
7
  def db_setup(opts={})
9
8
  use_time = opts[:use_time]
10
-
11
9
  DB.drop_table(:room_versions) if DB.table_exists?(:room_versions)
12
10
  DB.drop_table(:rooms) if DB.table_exists?(:rooms)
13
11
  DB.create_table! :rooms do
@@ -38,6 +36,7 @@ module DbHelpers
38
36
  bitemporal_options = {version_class: @version_class}
39
37
  bitemporal_options[:audit_class] = opts[:audit_class] if opts[:audit_class]
40
38
  bitemporal_options[:audit_updated_by_method] = opts[:audit_updated_by_method] if opts[:audit_updated_by_method]
39
+ bitemporal_options[:ranges] = opts[:ranges] if opts[:ranges]
41
40
 
42
41
  @master_class = Class.new Sequel::Model do
43
42
  set_dataset :rooms
@@ -46,7 +45,12 @@ module DbHelpers
46
45
  end
47
46
 
48
47
  def db_truncate
49
- @version_class.truncate
50
- @master_class.truncate
48
+ if DbHelpers.pg?
49
+ @version_class.truncate cascade: true
50
+ @master_class.truncate cascade: true
51
+ else
52
+ @version_class.truncate
53
+ @master_class.truncate
54
+ end
51
55
  end
52
56
  end