euston-eventstore 1.0.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.
Files changed (32) hide show
  1. data/Rakefile +118 -0
  2. data/euston-eventstore.gemspec +66 -0
  3. data/lib/euston-eventstore.rb +7 -0
  4. data/lib/euston-eventstore/commit.rb +77 -0
  5. data/lib/euston-eventstore/constants.rb +5 -0
  6. data/lib/euston-eventstore/dispatcher/asynchronous_dispatcher.rb +37 -0
  7. data/lib/euston-eventstore/dispatcher/null_dispatcher.rb +11 -0
  8. data/lib/euston-eventstore/dispatcher/synchronous_dispatcher.rb +21 -0
  9. data/lib/euston-eventstore/errors.rb +21 -0
  10. data/lib/euston-eventstore/event_message.rb +26 -0
  11. data/lib/euston-eventstore/optimistic_event_store.rb +68 -0
  12. data/lib/euston-eventstore/optimistic_event_stream.rb +106 -0
  13. data/lib/euston-eventstore/persistence/mongodb/mongo_commit.rb +82 -0
  14. data/lib/euston-eventstore/persistence/mongodb/mongo_commit_id.rb +16 -0
  15. data/lib/euston-eventstore/persistence/mongodb/mongo_config.rb +28 -0
  16. data/lib/euston-eventstore/persistence/mongodb/mongo_event_message.rb +31 -0
  17. data/lib/euston-eventstore/persistence/mongodb/mongo_persistence_engine.rb +167 -0
  18. data/lib/euston-eventstore/persistence/mongodb/mongo_persistence_factory.rb +31 -0
  19. data/lib/euston-eventstore/persistence/mongodb/mongo_snapshot.rb +32 -0
  20. data/lib/euston-eventstore/persistence/mongodb/mongo_stream_head.rb +29 -0
  21. data/lib/euston-eventstore/persistence/stream_head.rb +23 -0
  22. data/lib/euston-eventstore/snapshot.rb +21 -0
  23. data/lib/euston-eventstore/version.rb +5 -0
  24. data/spec/event_store/dispatcher/asynchronous_dispatcher_spec.rb +75 -0
  25. data/spec/event_store/dispatcher/synchronous_dispatcher_spec.rb +39 -0
  26. data/spec/event_store/optimistic_event_store_spec.rb +292 -0
  27. data/spec/event_store/optimistic_event_stream_spec.rb +318 -0
  28. data/spec/event_store/persistence/mongodb_spec.rb +301 -0
  29. data/spec/event_store/serialization/simple_message.rb +12 -0
  30. data/spec/spec_helper.rb +39 -0
  31. data/spec/support/array_enumeration_counter.rb +20 -0
  32. metadata +189 -0
@@ -0,0 +1,318 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe Euston::EventStore do
4
+ let(:uuid) { Uuid }
5
+ let(:default_stream_revision) { 1 }
6
+ let(:default_commit_sequence) { 1 }
7
+ let(:stream_id) { uuid.generate }
8
+ let(:persistence) { double('persistence') }
9
+ let(:stream) { Euston::EventStore::OptimisticEventStream.new(:stream_id => stream_id, :persistence => persistence) }
10
+
11
+ after { stream_id = uuid.generate }
12
+
13
+ def build_commit_stub(stream_id, revision, sequence, length)
14
+ Euston::EventStore::Commit.new( :stream_id => stream_id,
15
+ :stream_revision => revision,
16
+ :commit_sequence => sequence,
17
+ :events => length.times.map{ Euston::EventStore::EventMessage.new })
18
+ end
19
+
20
+ describe 'optimistic event stream' do
21
+ context 'when constructing a new stream' do
22
+ let(:min_revision) { 2 }
23
+ let(:max_revision) { 7 }
24
+ let(:commit_length) { 2 }
25
+ let(:committed) { [
26
+ build_commit_stub(stream_id, 2, 1, commit_length),
27
+ build_commit_stub(stream_id, 4, 2, commit_length),
28
+ build_commit_stub(stream_id, 6, 3, commit_length),
29
+ build_commit_stub(stream_id, 8, 3, commit_length)
30
+ ] }
31
+
32
+ before do
33
+ persistence.stub(:get_from).with(stream_id, min_revision, max_revision) { committed }
34
+ @stream = Euston::EventStore::OptimisticEventStream.new(:stream_id => stream_id,
35
+ :persistence => persistence,
36
+ :min_revision => min_revision,
37
+ :max_revision => max_revision)
38
+ end
39
+
40
+ it 'has the correct stream identifier' do
41
+ @stream.stream_id.should == stream_id
42
+ end
43
+
44
+ it 'has the correct head stream revision' do
45
+ @stream.stream_revision.should == max_revision
46
+ end
47
+
48
+ it 'has the correct head commit sequence' do
49
+ @stream.commit_sequence.should == committed.last.commit_sequence
50
+ end
51
+
52
+ it 'does not include the event below the minimum revision indicated' do
53
+ @stream.committed_events.first.should == committed.first.events.last
54
+ end
55
+
56
+ it 'does not include events above the maximum revision indicated' do
57
+ @stream.committed_events.last.should == committed.last.events.first
58
+ end
59
+
60
+ it 'has all of the committed events up to the stream revision specified' do
61
+ @stream.committed_events.length.should == max_revision - min_revision + 1
62
+ end
63
+ end
64
+
65
+ context 'when constructing the head event revision is less than the max desired revision' do
66
+ let(:commit_length) { 2 }
67
+ let(:committed) { [
68
+ build_commit_stub(stream_id, 2, 1, commit_length),
69
+ build_commit_stub(stream_id, 4, 2, commit_length),
70
+ build_commit_stub(stream_id, 6, 3, commit_length),
71
+ build_commit_stub(stream_id, 8, 3, commit_length)
72
+ ] }
73
+
74
+ before do
75
+ persistence.stub(:get_from).with(stream_id, 0, Euston::EventStore::FIXNUM_MAX) { committed }
76
+ @stream = Euston::EventStore::OptimisticEventStream.new(:stream_id => stream_id,
77
+ :persistence => persistence,
78
+ :min_revision => 0,
79
+ :max_revision => Euston::EventStore::FIXNUM_MAX)
80
+ end
81
+
82
+ it 'sets the stream revision to the revision of the most recent event' do
83
+ @stream.stream_revision.should == committed.last.stream_revision
84
+ end
85
+ end
86
+
87
+ context 'when adding a null event message' do
88
+ before do
89
+ stream << nil
90
+ end
91
+
92
+ it 'is ignored' do
93
+ stream.uncommitted_events.should be_empty
94
+ end
95
+ end
96
+
97
+ context 'when adding an unpopulated event message' do
98
+ before do
99
+ stream << Euston::EventStore::EventMessage.new(nil)
100
+ end
101
+
102
+ it 'is ignored' do
103
+ stream.uncommitted_events.should be_empty
104
+ end
105
+ end
106
+
107
+ context 'when adding a fully populated event message' do
108
+ before do
109
+ stream << Euston::EventStore::EventMessage.new('populated')
110
+ end
111
+
112
+ it 'adds the event to the set of uncommitted events' do
113
+ stream.uncommitted_events.should have(1).items
114
+ end
115
+ end
116
+
117
+ context 'when adding multiple populated event messages' do
118
+ before do
119
+ stream << Euston::EventStore::EventMessage.new('populated')
120
+ stream << Euston::EventStore::EventMessage.new('also populated')
121
+ end
122
+
123
+ it 'adds all the events provided to the set of uncommitted events' do
124
+ stream.uncommitted_events.should have(2).items
125
+ end
126
+ end
127
+
128
+ context 'when adding a simple object as an event message' do
129
+ let(:my_event) { 'some event data' }
130
+
131
+ before do
132
+ stream << Euston::EventStore::EventMessage.new(my_event)
133
+ end
134
+
135
+ it 'adds the uncommitted event to the set of uncommitted events' do
136
+ stream.uncommitted_events.should have(1).items
137
+ end
138
+
139
+ it 'wraps the uncommitted event in an EventMessage object' do
140
+ stream.uncommitted_events.first.body.should == my_event
141
+ end
142
+ end
143
+
144
+ context 'when clearing any uncommitted changes' do
145
+ before do
146
+ stream << Euston::EventStore::EventMessage.new('')
147
+ stream.clear_changes
148
+ end
149
+
150
+ it 'clears all uncommitted events' do
151
+ stream.uncommitted_events.should be_empty
152
+ end
153
+ end
154
+
155
+ context 'when committing an empty changeset' do
156
+ before do
157
+ persistence.stub(:commit) { @persisted = true }
158
+ stream.commit_changes uuid.generate
159
+ end
160
+
161
+ it 'does not call the underlying infrastructure' do
162
+ @persisted.should be_nil
163
+ end
164
+
165
+ it 'does not increment the current stream revision' do
166
+ stream.stream_revision.should == 0
167
+ end
168
+
169
+ it 'does not increment the current commit sequence' do
170
+ stream.commit_sequence.should == 0
171
+ end
172
+ end
173
+
174
+ context 'when committing any uncommitted changes' do
175
+ let(:commit_id) { uuid.generate }
176
+ let(:uncommitted) { Euston::EventStore::EventMessage.new '' }
177
+ let(:headers) { { :key => :value } }
178
+
179
+ before do
180
+ persistence.stub(:commit) { |c| @constructed = c }
181
+ stream << uncommitted
182
+ headers.each { |key, value| stream.uncommitted_headers[key] = value }
183
+ stream.commit_changes commit_id
184
+ end
185
+
186
+ it 'provides a commit to the underlying infrastructure' do
187
+ @constructed.should_not be_nil
188
+ end
189
+
190
+ it 'builds the commit with the correct stream identifier' do
191
+ @constructed.stream_id.should == stream_id
192
+ end
193
+
194
+ it 'builds the commit with the correct stream revision' do
195
+ @constructed.stream_revision.should == default_stream_revision
196
+ end
197
+
198
+ it 'builds the commit with the correct commit identifier' do
199
+ @constructed.commit_id.should == commit_id
200
+ end
201
+
202
+ it 'builds the commit with an incremented commit sequence' do
203
+ @constructed.commit_sequence.should == default_commit_sequence
204
+ end
205
+
206
+ it 'builds the commit with the correct commit stamp' do
207
+ ((Time.now - @constructed.commit_timestamp) < 0.05).should be_true
208
+ end
209
+
210
+ it 'builds the commit with the headers provided' do
211
+ @constructed.headers.each do |key, value|
212
+ headers[key].should == value
213
+ end
214
+ @constructed.headers.keys.length.should == headers.keys.length
215
+ end
216
+
217
+ it 'builds the commit containing all uncommitted events' do
218
+ @constructed.events.should have(1).items
219
+ end
220
+
221
+ it 'builds the commit using the event messages provided' do
222
+ @constructed.events.first.should == uncommitted
223
+ end
224
+
225
+ it 'updates the stream revision' do
226
+ stream.stream_revision.should == @constructed.stream_revision
227
+ end
228
+
229
+ it 'updates the commit sequence' do
230
+ stream.commit_sequence.should == @constructed.commit_sequence
231
+ end
232
+
233
+ it 'adds the uncommitted events to the committed events' do
234
+ stream.committed_events.last.should == uncommitted
235
+ end
236
+
237
+ it 'clears the uncommitted events' do
238
+ stream.uncommitted_events.should have(0).items
239
+ end
240
+
241
+ it 'clears the uncommitted headers' do
242
+ stream.uncommitted_headers.should have(0).items
243
+ end
244
+ end
245
+
246
+ context 'when committing with an identifier that was previously read' do
247
+ let(:committed) { [ build_commit_stub(stream_id, 1, 1, 1) ] }
248
+ let(:duplicate_commit_id) { committed.first.commit_id }
249
+
250
+ before do
251
+ persistence.stub(:get_from).with(stream_id, 0, Euston::EventStore::FIXNUM_MAX) { committed }
252
+
253
+ @stream = Euston::EventStore::OptimisticEventStream.new(:stream_id => stream_id,
254
+ :persistence => persistence,
255
+ :min_revision => 0,
256
+ :max_revision => Euston::EventStore::FIXNUM_MAX)
257
+
258
+ begin
259
+ @stream.commit_changes duplicate_commit_id
260
+ rescue Exception => e
261
+ @thrown = e
262
+ end
263
+ end
264
+
265
+ it 'throws a DuplicateCommitError' do
266
+ @thrown.should be_a(Euston::EventStore::DuplicateCommitError)
267
+ end
268
+ end
269
+
270
+ context 'when committing after another thread or process has moved the stream head' do
271
+ let(:stream_revision) { 1 }
272
+ let(:committed) { [ build_commit_stub(stream_id, 1, 1, 1) ] }
273
+ let(:uncommitted) { Euston::EventStore::EventMessage.new '' }
274
+ let(:discovered_on_commit) { [ build_commit_stub(stream_id, 3, 2, 2) ] }
275
+
276
+ before do
277
+ persistence.stub(:commit) { raise Euston::EventStore::ConcurrencyError.new }
278
+ persistence.stub(:get_from).with(stream_id, stream_revision, Euston::EventStore::FIXNUM_MAX) { committed }
279
+ persistence.stub(:get_from).with(stream_id, stream_revision + 1, Euston::EventStore::FIXNUM_MAX) do
280
+ @queried_for_new_commits = true
281
+ discovered_on_commit
282
+ end
283
+
284
+ @stream = Euston::EventStore::OptimisticEventStream.new(:stream_id => stream_id,
285
+ :persistence => persistence,
286
+ :min_revision => stream_revision,
287
+ :max_revision => Euston::EventStore::FIXNUM_MAX)
288
+ @stream << uncommitted
289
+
290
+ begin
291
+ @stream.commit_changes uuid.generate
292
+ rescue Exception => e
293
+ @thrown = e
294
+ end
295
+ end
296
+
297
+ it 'throws a ConcurrencyError' do
298
+ @thrown.should be_a(Euston::EventStore::ConcurrencyError)
299
+ end
300
+
301
+ it 'queries the underlying storage to discover the new commits' do
302
+ @queried_for_new_commits.should be_true
303
+ end
304
+
305
+ it 'updates the stream revision accordingly' do
306
+ @stream.stream_revision.should == discovered_on_commit.first.stream_revision
307
+ end
308
+
309
+ it 'updates the commit sequence accordingly' do
310
+ @stream.commit_sequence.should == discovered_on_commit.first.commit_sequence
311
+ end
312
+
313
+ it 'add the newly discovered committed events to the set of committed events accordingly' do
314
+ @stream.committed_events.should have(discovered_on_commit.first.events.length + 1).items
315
+ end
316
+ end
317
+ end
318
+ end
@@ -0,0 +1,301 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
+
3
+ describe Euston::EventStore do
4
+ describe 'mongodb persistence' do
5
+ let(:uuid) { Uuid }
6
+ let(:database) { 'event_store_tests' }
7
+ let(:config) { Euston::EventStore::Persistence::Mongodb::Config }
8
+ let(:factory) { Euston::EventStore::Persistence::Mongodb::MongoPersistenceFactory }
9
+ let(:stream_id) { uuid.generate }
10
+
11
+ before do
12
+ config.instance.database = database
13
+
14
+ @persistence = factory.build
15
+ @persistence.init
16
+ end
17
+
18
+ context 'when a commit is successfully persisted' do
19
+ let(:now) { Time.now.utc + (60 * 60 * 24 * 7 * 52) }
20
+ let(:attempt) { new_attempt(:commit_timestamp => now) }
21
+
22
+ before do
23
+ @persistence.commit attempt
24
+
25
+ sleep 0.25
26
+
27
+ @persisted = @persistence.get_from(:stream_id => stream_id,
28
+ :min_revision => 0,
29
+ :max_revision => Euston::EventStore::FIXNUM_MAX).first
30
+ end
31
+
32
+ it('correctly persists the stream identifier') { @persisted.stream_id.should == attempt.stream_id }
33
+ it('correctly persists the stream revision') { @persisted.stream_revision.should == attempt.stream_revision }
34
+ it('correctly persists the commit identifier') { @persisted.commit_id.should == attempt.commit_id }
35
+ it('correctly persists the commit sequence') { @persisted.commit_sequence.should == attempt.commit_sequence }
36
+
37
+ # persistence engines have varying levels of precision with respect to time.
38
+ it('correctly persists the commit timestamp') { (@persisted.commit_timestamp - now.to_f).should be <= 1 }
39
+
40
+ it('correctly persists the headers') { @persisted.headers.should have(attempt.headers.length).items }
41
+ it('correctly persists the events') { @persisted.events.should have(attempt.events.length).items }
42
+ it('makes the commit available to be read from the stream') {
43
+ @persistence.get_from(:stream_id => stream_id,
44
+ :min_revision => 0,
45
+ :max_revision => Euston::EventStore::FIXNUM_MAX).first.commit_id.should == attempt.commit_id }
46
+
47
+ it('adds the commit to the set of undispatched commits') {
48
+ @persistence.get_undispatched_commits.detect { |x| x.commit_id == attempt.commit_id }.should_not be_nil }
49
+
50
+ it('causes the stream to be found in the list of streams to snapshot') {
51
+ @persistence.get_streams_to_snapshot(1).detect { |x| x.stream_id == stream_id }.should_not be_nil }
52
+ end
53
+
54
+ context 'when reading from a given revision' do
55
+ let(:load_from_commit_containing_revision) { 3 }
56
+ let(:up_to_commit_containing_revision) { 5 }
57
+ let(:oldest) { new_attempt }
58
+ let(:oldest2) { next_attempt(oldest) }
59
+ let(:oldest3) { next_attempt(oldest2) }
60
+ let(:newest) { next_attempt(oldest3) }
61
+
62
+ before do
63
+ @persistence.commit oldest
64
+ @persistence.commit oldest2
65
+ @persistence.commit oldest3
66
+ @persistence.commit newest
67
+
68
+ @committed = @persistence.get_from(:stream_id => stream_id,
69
+ :min_revision => load_from_commit_containing_revision,
70
+ :max_revision => up_to_commit_containing_revision).to_a
71
+
72
+ end
73
+
74
+ it('starts from the commit which contains the minimum stream revision specified') { @committed.first.commit_id.should == oldest2.commit_id }
75
+ it('reads up to the commit which contains the maximum stream revision specified') { @committed.last.commit_id.should == oldest3.commit_id }
76
+ end
77
+
78
+ context 'when committing a stream with the same revision' do
79
+ let(:persistence1) { factory.build }
80
+ let(:persistence2) { factory.build }
81
+ let(:attempt1) { new_attempt }
82
+ let(:attempt2) { new_attempt }
83
+
84
+ before do
85
+ persistence1.init
86
+ persistence2.init
87
+
88
+ persistence1.commit attempt1
89
+
90
+ begin
91
+ persistence2.commit attempt2
92
+ rescue Exception => e
93
+ @caught = e
94
+ end
95
+ end
96
+
97
+ it('raises a ConcurrencyError') { @caught.should be_an(Euston::EventStore::ConcurrencyError) }
98
+ end
99
+
100
+ context 'when committing a stream with the same sequence' do
101
+ let(:persistence1) { factory.build }
102
+ let(:persistence2) { factory.build }
103
+ let(:attempt1) { new_attempt }
104
+ let(:attempt2) { new_attempt }
105
+
106
+ before do
107
+ persistence1.init
108
+ persistence2.init
109
+
110
+ persistence1.commit attempt1
111
+
112
+ begin
113
+ persistence2.commit attempt2
114
+ rescue Exception => e
115
+ @caught = e
116
+ end
117
+ end
118
+
119
+ it('raises a ConcurrencyError') { @caught.should be_an(Euston::EventStore::ConcurrencyError) }
120
+ end
121
+
122
+ context 'when attempting to overwrite a committed sequence' do
123
+ let(:successful_attempt) { new_attempt }
124
+ let(:failed_attempt) { new_attempt }
125
+
126
+ before do
127
+ @persistence.commit successful_attempt
128
+
129
+ begin
130
+ @persistence.commit failed_attempt
131
+ rescue Exception => e
132
+ @caught = e
133
+ end
134
+ end
135
+
136
+ it('raises a ConcurrencyError') { @caught.should be_an(Euston::EventStore::ConcurrencyError) }
137
+ end
138
+
139
+ context 'when attempting to persist a commit twice' do
140
+ let(:attempt) { new_attempt }
141
+
142
+ before do
143
+ @persistence.commit attempt
144
+
145
+ begin
146
+ @persistence.commit attempt
147
+ rescue Exception => e
148
+ @caught = e
149
+ end
150
+ end
151
+
152
+ it('raises a DuplicateCommitError') { @caught.should be_an(Euston::EventStore::DuplicateCommitError) }
153
+ end
154
+
155
+ context 'when a commit has been marked as dispatched' do
156
+ let(:attempt) { new_attempt }
157
+
158
+ before do
159
+ @persistence.commit attempt
160
+ @persistence.mark_commit_as_dispatched attempt
161
+ end
162
+
163
+ it('is no longer found in the set of undispatched commits') {
164
+ @persistence.get_undispatched_commits.detect { |c| c.commit_id == attempt.commit_id }.should be_nil }
165
+ end
166
+
167
+ context 'when saving a snapshot' do
168
+ let(:snapshot) { Euston::EventStore::Snapshot.new stream_id, 1, { :key => :value } }
169
+
170
+ before do
171
+ @persistence.commit new_attempt
172
+
173
+ sleep 0.25
174
+
175
+ @added = @persistence.add_snapshot snapshot
176
+ end
177
+
178
+ it('indicates the snapshot was added') { @added.should be_true }
179
+ it('is able to retrieve the snapshot') { @persistence.get_snapshot(stream_id, snapshot.stream_revision).should_not be_nil }
180
+ end
181
+
182
+ context 'when retrieving a snapshot' do
183
+ let(:too_far_back) { Euston::EventStore::Snapshot.new stream_id, 1, {} }
184
+ let(:correct) { Euston::EventStore::Snapshot.new stream_id, 3, { 'key' => 'value' } }
185
+ let(:too_far_forward) { Euston::EventStore::Snapshot.new stream_id, 5, {} }
186
+ let(:commit1) { new_attempt }
187
+ let(:commit2) { next_attempt commit1 }
188
+ let(:commit3) { next_attempt commit2 }
189
+
190
+ before do
191
+ @persistence.commit commit1
192
+ @persistence.commit commit2
193
+ @persistence.commit commit3
194
+
195
+ sleep 0.25
196
+
197
+ @persistence.add_snapshot too_far_back
198
+ @persistence.add_snapshot correct
199
+ @persistence.add_snapshot too_far_forward
200
+
201
+ @snapshot = @persistence.get_snapshot stream_id, too_far_forward.stream_revision - 1
202
+ end
203
+
204
+ it('loads the most recent prior snapshot') { @snapshot.stream_revision.should == correct.stream_revision }
205
+ it('has the correct snapshot payload') { @snapshot.payload.should == correct.payload }
206
+ end
207
+
208
+ context 'when a snapshot has been added to the most recent commit of a stream' do
209
+ let(:oldest) { new_attempt }
210
+ let(:oldest2) { next_attempt oldest }
211
+ let(:newest) { next_attempt oldest2 }
212
+ let(:snapshot) { Euston::EventStore::Snapshot.new stream_id, newest.stream_revision, { :key => :value } }
213
+
214
+ before do
215
+ @persistence.commit oldest
216
+ @persistence.commit oldest2
217
+ @persistence.commit newest
218
+
219
+ sleep 0.25
220
+
221
+ @persistence.add_snapshot snapshot
222
+ end
223
+
224
+ it('no longer finds the stream in the set of streams to be snapshot') {
225
+ @persistence.get_streams_to_snapshot(1).detect { |x| x.stream_id == stream_id }.should be_nil }
226
+ end
227
+
228
+ # Timing issues with this one?
229
+ #
230
+ # context 'when adding a commit after a snapshot' do
231
+ # let(:within_threshold) { 2 }
232
+ # let(:over_threshold) { 3 }
233
+ # let(:snapshot_data) { { :key => :value } }
234
+ # let(:oldest) { new_attempt }
235
+ # let(:oldest2) { next_attempt oldest }
236
+ # let(:newest) { next_attempt oldest2 }
237
+
238
+ # before do
239
+ # @persistence.commit oldest
240
+ # @persistence.commit oldest2
241
+
242
+ # sleep 0.25
243
+
244
+ # @persistence.add_snapshot Euston::EventStore::Snapshot.new(stream_id, oldest2.stream_revision, snapshot_data)
245
+ # @persistence.commit newest
246
+ # end
247
+
248
+ # it 'finds the stream in the set of streams to be snapshot when within the threshold' do
249
+ # @persistence.get_streams_to_snapshot(within_threshold).detect { |x| x.stream_id == stream_id }.should_not be_nil
250
+ # end
251
+
252
+ # it 'does not find the stream in the set of stream to be snapshot when over the threshold' do
253
+ # @persistence.get_streams_to_snapshot(over_threshold).detect { |x| x.stream_id == stream_id }.should be_nil
254
+ # end
255
+ # end
256
+
257
+ context 'when reading all commits from a particular point in time' do
258
+ let(:now) { Time.now.utc + (60 * 60 * 24 * 7 * 52) }
259
+ let(:first) { new_attempt(:commit_timestamp => now + 1) }
260
+ let(:second) { next_attempt first }
261
+ let(:third) { next_attempt second }
262
+ let(:fourth) { next_attempt third }
263
+
264
+ before do
265
+ @persistence.commit first
266
+ @persistence.commit second
267
+ @persistence.commit third
268
+ @persistence.commit fourth
269
+
270
+ @committed = @persistence.get_from :timestamp => now
271
+ end
272
+
273
+ it('returns all commits on or after the point in time specified') { @committed.should have(4).items }
274
+ end
275
+
276
+ def new_attempt(options = {})
277
+ defaults = { :stream_id => stream_id,
278
+ :stream_revision => 2,
279
+ :commit_id => uuid.generate,
280
+ :commit_sequence => 1,
281
+ :commit_timestamp => Time.now.utc,
282
+ :headers => { 'A header' => 'A string value',
283
+ 'Another header' => 2 },
284
+ :events => [ Euston::EventStore::EventMessage.new(:some_property => 'test'),
285
+ Euston::EventStore::EventMessage.new(:some_property => 'test2') ] }
286
+
287
+ Euston::EventStore::Commit.new(defaults.merge options)
288
+ end
289
+
290
+ def next_attempt(attempt)
291
+ Euston::EventStore::Commit.new(:stream_id => attempt.stream_id,
292
+ :stream_revision => attempt.stream_revision + 2,
293
+ :commit_id => uuid.generate,
294
+ :commit_sequence => attempt.commit_sequence + 1,
295
+ :commit_timestamp => attempt.commit_timestamp,
296
+ :headers => {},
297
+ :events => [ Euston::EventStore::EventMessage.new(:some_property => 'Another test'),
298
+ Euston::EventStore::EventMessage.new(:some_property => 'Another test2') ])
299
+ end
300
+ end
301
+ end