event-storage 0.7.2 → 0.9.1

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.
@@ -1,591 +0,0 @@
1
- const expect = require('expect.js');
2
- const fs = require('fs-extra');
3
- const EventStore = require('../src/EventStore');
4
-
5
- const storageDirectory = __dirname + '/data';
6
-
7
- describe('EventStore', function() {
8
-
9
- let eventstore;
10
-
11
- beforeEach(function () {
12
- fs.emptyDirSync(storageDirectory);
13
- });
14
-
15
- afterEach(function () {
16
- if (eventstore) {
17
- eventstore.close();
18
- }
19
- eventstore = null;
20
- });
21
-
22
- it('basically works', function(done) {
23
- eventstore = new EventStore({
24
- storageDirectory
25
- });
26
-
27
- let events = [{foo: 'bar'}, {foo: 'baz'}, {foo: 'quux'}];
28
- eventstore.on('ready', () => {
29
- eventstore.commit('foo-bar', events, () => {
30
- const stream = eventstore.getEventStream('foo-bar');
31
- let i = 0;
32
- for (let event of stream) {
33
- expect(event).to.eql(events[i++]);
34
- }
35
- done();
36
- });
37
- });
38
- });
39
-
40
- it('throws when scanning of stream directory fails', function() {
41
- const fs = require('fs');
42
- const originalReaddir = fs.readdir;
43
- fs.readdir = (dir, callback) => callback(new Error('Something went wrong!'), null);
44
-
45
- expect(() => new EventStore({
46
- storageDirectory
47
- })).to.throwError(/Something went wrong!/);
48
- fs.readdir = originalReaddir;
49
- });
50
-
51
- it('throws when trying to open non-existing store read-only', function() {
52
- expect(() => new EventStore({
53
- storageDirectory,
54
- readOnly: true
55
- })).to.throwError();
56
- });
57
-
58
- it('can open read-only', function(done) {
59
- eventstore = new EventStore({
60
- storageDirectory
61
- });
62
-
63
- let events = [{foo: 'bar'}, {foo: 'baz'}, {foo: 'quux'}];
64
- eventstore.on('ready', () => {
65
- eventstore.commit('foo-bar', events, () => {
66
- let readstore = new EventStore({
67
- storageDirectory,
68
- readOnly: true
69
- });
70
-
71
- readstore.on('ready', () => {
72
- const stream = readstore.getEventStream('foo-bar');
73
- let i = 0;
74
- for (let event of stream) {
75
- expect(event).to.eql(events[i++]);
76
- }
77
- readstore.close();
78
- done();
79
- });
80
- });
81
- });
82
- });
83
-
84
- describe('commit', function() {
85
-
86
- it('throws when no stream name specified', function() {
87
- eventstore = new EventStore({
88
- storageDirectory
89
- });
90
-
91
- expect(() => eventstore.commit({ foo: 'bar' })).to.throwError();
92
- });
93
-
94
- it('throws when no events specified', function() {
95
- eventstore = new EventStore({
96
- storageDirectory
97
- });
98
-
99
- expect(() => eventstore.commit('foo-bar')).to.throwError();
100
- });
101
-
102
- it('throws when opened in read-only mode', function() {
103
- eventstore = new EventStore({
104
- storageDirectory
105
- });
106
- eventstore.close();
107
-
108
- eventstore = new EventStore({
109
- storageDirectory,
110
- readOnly: true
111
- });
112
-
113
- expect(() => eventstore.commit('foo-bar', { foo: 'bar' })).to.throwError();
114
- });
115
-
116
- it('can commit a single event', function() {
117
- eventstore = new EventStore({
118
- storageDirectory
119
- });
120
-
121
- eventstore.commit('foo-bar', { foo: 'bar' });
122
- expect(eventstore.length).to.be(1);
123
- });
124
-
125
- it('can commit multiple events at once', function() {
126
- eventstore = new EventStore({
127
- storageDirectory
128
- });
129
-
130
- eventstore.commit('foo-bar', [{ foo: 'bar' }, { bar: 'baz' }, { baz: 'quux' }]);
131
- expect(eventstore.length).to.be(3);
132
- });
133
-
134
- it('invokes callback when finished', function(done) {
135
- eventstore = new EventStore({
136
- storageDirectory
137
- });
138
-
139
- eventstore.commit('foo-bar', [{ foo: 'bar' }], (commit) => {
140
- expect(eventstore.length).to.be(1);
141
- expect(commit.streamName).to.be('foo-bar');
142
- expect(commit.streamVersion).to.be(0);
143
- expect(commit.events).to.eql([{ foo: 'bar' }]);
144
- done();
145
- });
146
- });
147
-
148
- it('invokes callback when finished with optimistic concurrency check', function(done) {
149
- eventstore = new EventStore({
150
- storageDirectory
151
- });
152
-
153
- eventstore.commit('foo-bar', [{ foo: 'bar' }], EventStore.ExpectedVersion.EmptyStream, (commit) => {
154
- expect(eventstore.length).to.be(1);
155
- expect(commit.streamName).to.be('foo-bar');
156
- expect(commit.streamVersion).to.be(0);
157
- expect(commit.events).to.eql([{ foo: 'bar' }]);
158
- done();
159
- });
160
- });
161
-
162
- it('invokes callback when finished with optimistic concurrency check and metdata', function(done) {
163
- eventstore = new EventStore({
164
- storageDirectory
165
- });
166
-
167
- eventstore.commit('foo-bar', [{ foo: 'bar' }], EventStore.ExpectedVersion.EmptyStream, {}, (commit) => {
168
- expect(eventstore.length).to.be(1);
169
- expect(commit.streamName).to.be('foo-bar');
170
- expect(commit.streamVersion).to.be(0);
171
- expect(commit.events).to.eql([{ foo: 'bar' }]);
172
- done();
173
- });
174
- });
175
-
176
- it('invokes "commit" event when finished', function(done) {
177
- eventstore = new EventStore({
178
- storageDirectory
179
- });
180
-
181
- eventstore.on('commit', (commit) => {
182
- expect(eventstore.length).to.be(1);
183
- expect(commit.streamName).to.be('foo-bar');
184
- expect(commit.streamVersion).to.be(0);
185
- expect(commit.events).to.eql([{ foo: 'bar' }]);
186
- done();
187
- });
188
- eventstore.commit('foo-bar', [{ foo: 'bar' }]);
189
- });
190
-
191
- it('throws an optimistic concurrency error if stream version does not match', function(done) {
192
- eventstore = new EventStore({
193
- storageDirectory
194
- });
195
-
196
- expect(() => eventstore.commit('foo-bar', { foo: 'bar' }, 1)).to.throwError(
197
- e => expect(e).to.be.a(EventStore.OptimisticConcurrencyError)
198
- );
199
-
200
- eventstore.commit('foo-bar', { foo: 'bar' }, () => {
201
- expect(() => eventstore.commit('foo-bar', { foo: 'baz' }, EventStore.ExpectedVersion.EmptyStream)).to.throwError(
202
- e => expect(e).to.be.a(EventStore.OptimisticConcurrencyError)
203
- );
204
- expect(() => eventstore.commit('foo-bar', { foo: 'baz' }, 2)).to.throwError(
205
- e => expect(e).to.be.a(EventStore.OptimisticConcurrencyError)
206
- );
207
- done();
208
- });
209
- });
210
-
211
- it('does not throw an optimistic concurrency error if stream version matches', function(done) {
212
- eventstore = new EventStore({
213
- storageDirectory
214
- });
215
-
216
- expect(() => eventstore.commit('foo-bar', { foo: 'bar' }, EventStore.ExpectedVersion.EmptyStream)).to.not.throwError();
217
-
218
- eventstore.commit('foo-bar', { foo: 'bar' }, () => {
219
- expect(() => eventstore.commit('foo-bar', {foo: 'baz'}, 2)).to.not.throwError();
220
- expect(() => eventstore.commit('foo-bar', {foo: 'baz'}, EventStore.ExpectedVersion.Any)).to.not.throwError();
221
- done();
222
- });
223
- });
224
-
225
- it('uses metadata from argument for commit', function(done) {
226
- eventstore = new EventStore({
227
- storageDirectory
228
- });
229
-
230
- eventstore.commit('foo-bar', [{ foo: 'bar' }], { commitId: 1, committedAt: 12345, quux: 'quux' }, (commit) => {
231
- expect(commit.commitId).to.be(1);
232
- expect(commit.committedAt).to.be(12345);
233
- expect(commit.quux).to.be('quux');
234
-
235
- const stream = eventstore.getEventStream('foo-bar');
236
- const storedEvent = stream.next();
237
- expect(storedEvent.metadata.commitId).to.be(1);
238
- expect(storedEvent.metadata.committedAt).to.be(12345);
239
- expect(storedEvent.metadata.quux).to.be('quux');
240
- done();
241
- });
242
- });
243
-
244
- });
245
-
246
- describe('createEventStream', function() {
247
-
248
- it('throws when trying to recreate existing stream', function(done) {
249
- eventstore = new EventStore({
250
- storageDirectory
251
- });
252
-
253
- eventstore.commit('foo-bar', [{ type: 'foo' }], () => {
254
- expect(() => eventstore.createEventStream('foo-bar', event => event.payload.type === 'foo')).to.throwError();
255
- done();
256
- });
257
- });
258
-
259
- it('can create new streams on existing events', function(done) {
260
- eventstore = new EventStore({
261
- storageDirectory
262
- });
263
-
264
- eventstore.commit('foo-bar', [{ type: 'foo' }], () => {
265
- const stream = eventstore.createEventStream('my-foo-bar', event => event.payload.type === 'foo');
266
- expect(stream.events.length).to.be(1);
267
- expect(stream.events[0]).to.eql({ type: 'foo' });
268
- done();
269
- });
270
- });
271
-
272
- });
273
-
274
- describe('getStreamVersion', function() {
275
-
276
- it('returns -1 if the stream does not exist', function() {
277
- eventstore = new EventStore({
278
- storageDirectory
279
- });
280
- expect(eventstore.getStreamVersion('foo')).to.be(-1);
281
- });
282
-
283
- it('returns 0 if the stream is empty', function() {
284
- eventstore = new EventStore({
285
- storageDirectory
286
- });
287
- eventstore.createEventStream('foo', () => true);
288
- expect(eventstore.getStreamVersion('foo')).to.be(0);
289
- });
290
-
291
- it('returns the version of the stream', function(done) {
292
- eventstore = new EventStore({
293
- storageDirectory
294
- });
295
- eventstore.commit('foo', [{ type: 'foo' }], () => {
296
- expect(eventstore.getStreamVersion('foo')).to.be(1);
297
- done();
298
- });
299
- });
300
-
301
- });
302
-
303
- describe('getEventStream', function() {
304
-
305
- it('can open existing streams', function(done) {
306
- eventstore = new EventStore({
307
- storageDirectory
308
- });
309
-
310
- eventstore.commit('foo-bar', [{ foo: 'bar' }]);
311
- eventstore.close();
312
-
313
- eventstore = new EventStore({
314
- storageDirectory
315
- });
316
- eventstore.on('ready', () => {
317
- const stream = eventstore.getEventStream('foo-bar');
318
- expect(stream.events.length).to.be(1);
319
- done();
320
- });
321
- });
322
-
323
- it('can iterate events in reverse order', function() {
324
- eventstore = new EventStore({
325
- storageDirectory
326
- });
327
-
328
- for (let i=1; i<=20; i++) {
329
- eventstore.commit('foo-bar', [{key: i}]);
330
- }
331
-
332
- let reverseStream = eventstore.getEventStream('foo-bar', -1, 0);
333
- let i = 20;
334
- for (let event of reverseStream) {
335
- expect(event).to.eql({ key: i-- });
336
- }
337
- });
338
-
339
- it('needs to be tested further.');
340
- });
341
-
342
- describe('fromStreams', function() {
343
-
344
- it('throws when not specifying a join stream name', function() {
345
- eventstore = new EventStore({
346
- storageDirectory
347
- });
348
-
349
- expect(() => eventstore.fromStreams()).to.throwError();
350
- });
351
-
352
- it('throws when not specifying an array of stream names to join', function() {
353
- eventstore = new EventStore({
354
- storageDirectory
355
- });
356
-
357
- expect(() => eventstore.fromStreams('join-foo-bar')).to.throwError();
358
- });
359
-
360
- it('throws when specifying a non-existing stream to join', function() {
361
- eventstore = new EventStore({
362
- storageDirectory
363
- });
364
-
365
- expect(() => eventstore.fromStreams('join-foo-bar', ['foo-bar', 'baz'])).to.throwError(/does not exist/);
366
- });
367
-
368
- it('iterates events from multiple streams in correct order', function(done) {
369
- eventstore = new EventStore({
370
- storageDirectory
371
- });
372
-
373
- eventstore.commit('foo', { key: 1 }, () => {
374
- eventstore.commit('bar', { key: 2}, () => {
375
- eventstore.commit('foo', { key: 3 }, () => {
376
- eventstore.commit('bar', { key: 4}, () => {
377
- let joinStream = eventstore.fromStreams('foobar', ['foo','bar']);
378
- let key = 1;
379
- for (let event of joinStream) {
380
- expect(event.key).to.be(key);
381
- key++;
382
- }
383
- expect(key).to.be(5);
384
- done();
385
- });
386
- });
387
- });
388
- });
389
- });
390
-
391
- it('iterates events from multiple streams in reverse order', function() {
392
- eventstore = new EventStore({
393
- storageDirectory
394
- });
395
-
396
- for (let i=1; i<=20; i++) {
397
- eventstore.commit(i % 2 ? 'foo' : 'bar', [{key: i}]);
398
- }
399
-
400
- let reverseStream = eventstore.fromStreams('foo-bar', ['foo', 'bar'],-1, 0);
401
- let i = 20;
402
- for (let event of reverseStream) {
403
- expect(event).to.eql({ key: i-- });
404
- }
405
- });
406
-
407
- });
408
-
409
- describe('createEventStream', function() {
410
-
411
- it('throws in read-only mode', function () {
412
- eventstore = new EventStore({
413
- storageDirectory
414
- });
415
-
416
- let readstore = new EventStore({
417
- storageDirectory,
418
- readOnly: true
419
- });
420
- expect(() => readstore.createEventStream('foo-bar', () => true)).to.throwError();
421
- readstore.close();
422
- });
423
-
424
- it('throws when trying to re-create stream', function () {
425
- eventstore = new EventStore({
426
- storageDirectory
427
- });
428
- eventstore.createEventStream('foo-bar', () => true)
429
-
430
- expect(() => eventstore.createEventStream('foo-bar', () => true)).to.throwError();
431
- });
432
- });
433
-
434
- describe('deleteEventStream', function() {
435
-
436
- it('throws in read-only mode', function(done) {
437
- eventstore = new EventStore({
438
- storageDirectory
439
- });
440
- eventstore.createEventStream('foo-bar', () => true);
441
-
442
- let readstore = new EventStore({
443
- storageDirectory,
444
- readOnly: true
445
- });
446
- readstore.on('ready', () => {
447
- expect(() => readstore.deleteEventStream('foo-bar')).to.throwError();
448
- readstore.close();
449
- done();
450
- });
451
- });
452
-
453
- it('removes the stream persistently', function(done) {
454
- eventstore = new EventStore({
455
- storageDirectory
456
- });
457
-
458
- eventstore.commit('foo-bar', [{ foo: 'bar' }], () => {
459
- expect(fs.existsSync(storageDirectory + '/streams/eventstore.stream-foo-bar.index')).to.be(true);
460
- eventstore.deleteEventStream('foo-bar');
461
- expect(eventstore.getEventStream('foo-bar')).to.be(false);
462
- expect(fs.existsSync(storageDirectory + '/streams/eventstore.stream-foo-bar.index')).to.be(false);
463
- done();
464
- });
465
- });
466
-
467
- it('is noop for non-existing stream', function(done) {
468
- eventstore = new EventStore({
469
- storageDirectory
470
- });
471
-
472
- eventstore.commit('foo-bar', [{ foo: 'bar' }], () => {
473
- eventstore.deleteEventStream('bar');
474
- expect(eventstore.getEventStream('foo-bar')).to.not.be(false);
475
- done();
476
- });
477
- });
478
-
479
- });
480
-
481
- describe('getCommits', function() {
482
-
483
- function commitAll(streamName, commits, callback) {
484
- for (let i = 0; i < commits.length; i++) {
485
- eventstore.commit(streamName, commits[i], i === commits.length - 1 ? callback : undefined);
486
- }
487
- }
488
-
489
- it('returns empty iterator if no commits in store', function() {
490
- eventstore = new EventStore({
491
- storageDirectory
492
- });
493
-
494
- const commits = eventstore.getCommits(0);
495
- expect(commits.next()).to.eql({ value: undefined, done: true });
496
- });
497
-
498
- it('returns a list of all commits', function(done) {
499
- eventstore = new EventStore({
500
- storageDirectory
501
- });
502
- const events = [
503
- [{ foo: 1 }, { foo: 2 }],
504
- [{ bar: 1 }],
505
- [{ baz: 1 }, { baz: 2 }, { baz: 3 }]
506
- ];
507
- commitAll('foo-bar', events, () => {
508
- const commits = eventstore.getCommits(0);
509
- let streamVersion = 0;
510
- let commitNumber = 0;
511
- for (let commit of commits) {
512
- expect(commit.streamName).to.be('foo-bar');
513
- expect(commit.streamVersion).to.be(streamVersion);
514
- expect(commit.events).to.eql(events[commitNumber++]);
515
- streamVersion += commit.events.length;
516
- }
517
- expect(commitNumber).to.be(3);
518
- done();
519
- });
520
- });
521
-
522
- it('returns only commits after the given revision', function(done) {
523
- eventstore = new EventStore({
524
- storageDirectory
525
- });
526
- const events = [
527
- [{ foo: 1 }, { foo: 2 }],
528
- [{ bar: 1 }],
529
- [{ baz: 1 }, { baz: 2 }, { baz: 3 }]
530
- ];
531
- commitAll('foo-bar', events, () => {
532
- const commits = eventstore.getCommits(4);
533
- let streamVersion = 3;
534
- let commitNumber = 2;
535
- for (let commit of commits) {
536
- expect(commit.streamName).to.be('foo-bar');
537
- expect(commit.streamVersion).to.be(streamVersion);
538
- expect(commit.events).to.eql(events[commitNumber++]);
539
- streamVersion += commit.events.length;
540
- }
541
- expect(commitNumber).to.be(3);
542
- done();
543
- });
544
- });
545
-
546
- it('returns the full commit if given revision is within a single commit', function(done) {
547
- eventstore = new EventStore({
548
- storageDirectory
549
- });
550
- const events = [
551
- [{ foo: 1 }, { foo: 2 }],
552
- [{ bar: 1 }],
553
- [{ baz: 1 }, { baz: 2 }, { baz: 3 }]
554
- ];
555
- commitAll('foo-bar', events, () => {
556
- const commits = eventstore.getCommits(5);
557
- let streamVersion = 3;
558
- let commitNumber = 2;
559
- for (let commit of commits) {
560
- expect(commit.streamName).to.be('foo-bar');
561
- expect(commit.streamVersion).to.be(streamVersion);
562
- expect(commit.events).to.eql(events[commitNumber++]);
563
- streamVersion += commit.events.length;
564
- }
565
- expect(commitNumber).to.be(3);
566
- done();
567
- });
568
- });
569
-
570
- });
571
-
572
- describe('getConsumer', function() {
573
-
574
- it('returns a consumer for the given stream', function(done) {
575
- eventstore = new EventStore({
576
- storageDirectory
577
- });
578
- eventstore.createEventStream('foo-bar', event => event.payload.foo === 'bar');
579
-
580
- const consumer = eventstore.getConsumer('foo-bar', 'consumer1');
581
- consumer.on('data', event => {
582
- expect(event.id).to.be(2);
583
- done();
584
- });
585
- eventstore.commit('foo', { foo: 'baz', id: 1 });
586
- eventstore.commit('foo', { foo: 'bar', id: 2 });
587
- });
588
-
589
- });
590
-
591
- });
@@ -1,120 +0,0 @@
1
- const expect = require('expect.js');
2
- const EventStream = require('../src/EventStream');
3
-
4
- describe('EventStream', function() {
5
-
6
- let stream, mockEventStore;
7
- const events = ['foo', 'bar', 'baz'];
8
-
9
- beforeEach(function () {
10
- // This is pretty ugly testing internals, but that's only because we extracted this ugliness out of the EventStore
11
- mockEventStore = {
12
- streams: {
13
- 'foo': {
14
- index: {
15
- name: 'foo-index',
16
- length: events.length
17
- }
18
- }
19
- },
20
- storage: {
21
- *readRange(from, until = -1) {
22
- mockEventStore.storage.from = from;
23
- mockEventStore.storage.until = until;
24
- for (let event of events) {
25
- yield { stream: 'foo', payload: event, metadata: { occuredAt: 12345 } };
26
- }
27
- }
28
- }
29
- };
30
- stream = new EventStream('foo', mockEventStore);
31
- });
32
-
33
- it('makes the name available', function(){
34
- expect(stream.name).to.be('foo');
35
- });
36
-
37
- it('has version -1 if stream does not exist', function(){
38
- stream = new EventStream('foo-bar-baz', mockEventStore);
39
- expect(stream.version).to.be(-1);
40
- });
41
-
42
- it('makes the version available', function(){
43
- expect(stream.version).to.be(events.length);
44
- });
45
-
46
- it('adjusts the version to given maxRevision constraint', function(){
47
- stream = new EventStream('foo', mockEventStore, 0, -2);
48
- expect(stream.version).to.be(events.length - 1);
49
- });
50
-
51
- it('throws if no name specified in constructor', function(){
52
- expect(() => new EventStream()).to.throwError(/stream name/);
53
- });
54
-
55
- it('throws if empty name specified in constructor', function(){
56
- expect(() => new EventStream('')).to.throwError(/stream name/);
57
- });
58
-
59
- it('throws if no EventStore specified in constructor', function(){
60
- expect(() => new EventStream('foo')).to.throwError(/EventStore/);
61
- });
62
-
63
- it('makes all events accessible as array', function(){
64
- expect(stream.events).to.eql(events);
65
- });
66
-
67
- it('returns all events consistently', function(){
68
- expect(stream.events).to.eql(stream.events);
69
- });
70
-
71
- it('can be iterated with for .. of', function(){
72
- let i = 0;
73
- for (let event of stream) {
74
- expect(event).to.be(events[i++]);
75
- }
76
- });
77
-
78
- it('is a readable stream', function(){
79
- let i = 0;
80
- stream.on('data', (event) => {
81
- expect(event).to.be(events[i++]);
82
- });
83
- });
84
-
85
- it('adjusts revisions to 1-based index', function(){
86
- stream = new EventStream('foo', mockEventStore, 0, 1);
87
- const events = stream.events;
88
-
89
- expect(mockEventStore.storage.from).to.be(1);
90
- expect(mockEventStore.storage.until).to.be(2);
91
- });
92
-
93
- it('leaves negative revisions untouched', function(){
94
- stream = new EventStream('foo', mockEventStore, -1, -1);
95
- // read all and convert to array
96
- const events = stream.events;
97
-
98
- expect(mockEventStore.storage.from).to.be(-1);
99
- expect(mockEventStore.storage.until).to.be(-1);
100
- });
101
-
102
- it('is empty when stream does not exist', function(){
103
- stream = new EventStream('bar', mockEventStore);
104
- expect(stream.events).to.be.eql([]);
105
- });
106
-
107
- describe('forEach', function(){
108
-
109
- it('invokes a callback with payload, metadata and stream name', function(){
110
- let i = 0;
111
- stream.forEach((event, metadata, stream) => {
112
- expect(event).to.be(events[i++]);
113
- expect(metadata).to.eql({ occuredAt: 12345 });
114
- expect(stream).to.be('foo');
115
- });
116
- });
117
-
118
- });
119
-
120
- });