event-storage 0.8.0 → 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.
- package/README.md +51 -540
- package/index.js +5 -6
- package/package.json +28 -31
- package/src/Clock.js +21 -9
- package/src/Consumer.js +6 -7
- package/src/EventStore.js +261 -67
- package/src/EventStream.js +172 -19
- package/src/Index/ReadOnlyIndex.js +3 -3
- package/src/Index/ReadableIndex.js +17 -13
- package/src/Index/WritableIndex.js +17 -11
- package/src/Index.js +7 -5
- package/src/IndexEntry.js +2 -3
- package/src/JoinEventStream.js +34 -32
- package/src/Partition/ReadOnlyPartition.js +3 -3
- package/src/Partition/ReadablePartition.js +110 -57
- package/src/Partition/WritablePartition.js +81 -23
- package/src/Partition.js +7 -4
- package/src/Storage/ReadOnlyStorage.js +4 -4
- package/src/Storage/ReadableStorage.js +144 -22
- package/src/Storage/WritableStorage.js +175 -33
- package/src/Storage.js +9 -4
- package/src/Watcher.js +6 -5
- package/src/WatchesFile.js +8 -7
- package/src/metadataUtil.js +79 -0
- package/src/util.js +74 -73
- package/test/Consumer.spec.js +0 -455
- package/test/EventStore.spec.js +0 -632
- package/test/EventStream.spec.js +0 -120
- package/test/Index.spec.js +0 -591
- package/test/JoinEventStream.spec.js +0 -113
- package/test/Partition.spec.js +0 -488
- package/test/Storage.spec.js +0 -1017
- package/test/Watcher.spec.js +0 -131
package/test/EventStream.spec.js
DELETED
|
@@ -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
|
-
});
|
package/test/Index.spec.js
DELETED
|
@@ -1,591 +0,0 @@
|
|
|
1
|
-
const expect = require('expect.js');
|
|
2
|
-
const fs = require('fs-extra');
|
|
3
|
-
const Index = require('../src/Index');
|
|
4
|
-
|
|
5
|
-
const dataDirectory = __dirname + '/data';
|
|
6
|
-
|
|
7
|
-
describe('Index', function() {
|
|
8
|
-
|
|
9
|
-
let index, counter = 1, readers = [];
|
|
10
|
-
|
|
11
|
-
beforeEach(function() {
|
|
12
|
-
fs.emptyDirSync(dataDirectory);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
afterEach(function() {
|
|
16
|
-
if (index) index.close();
|
|
17
|
-
for (let reader of readers) reader.close();
|
|
18
|
-
readers = [];
|
|
19
|
-
index = null;
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
function createIndex(name = 'test.index', options = {}) {
|
|
23
|
-
return new Index(name, Object.assign({ dataDirectory }, options));
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function setupIndexWithEntries(num, indexMapper, options) {
|
|
27
|
-
if (typeof indexMapper === 'object') {
|
|
28
|
-
options = indexMapper;
|
|
29
|
-
indexMapper = null;
|
|
30
|
-
}
|
|
31
|
-
index = createIndex('test' + (counter++) + '.index', options);
|
|
32
|
-
for (let i = 1; i <= num; i++) {
|
|
33
|
-
index.add(new Index.Entry(indexMapper && indexMapper(i) || i, i));
|
|
34
|
-
}
|
|
35
|
-
index.flush();
|
|
36
|
-
return index;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function createReader(name, options) {
|
|
40
|
-
let reader = new Index.ReadOnly(name, Object.assign({ dataDirectory }, options));
|
|
41
|
-
readers[readers.length] = reader;
|
|
42
|
-
return reader;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
it('is opened on instantiation', function() {
|
|
46
|
-
index = setupIndexWithEntries();
|
|
47
|
-
expect(index.isOpen()).to.be(true);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('defaults name to ".index"', function() {
|
|
51
|
-
index = new Index({ dataDirectory });
|
|
52
|
-
expect(index.name).to.be('.index');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('recovers metadata on reopening', function() {
|
|
56
|
-
index = createIndex('.index', { metadata: { test: 'valueStays' } });
|
|
57
|
-
expect(index.metadata.test).to.be('valueStays');
|
|
58
|
-
index.close();
|
|
59
|
-
index = createIndex(index.name);
|
|
60
|
-
expect(index.metadata.test).to.be('valueStays');
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('throws on opening an non-index file', function() {
|
|
64
|
-
const indexFile = dataDirectory + '/.index';
|
|
65
|
-
fs.writeFileSync(indexFile, 'foo');
|
|
66
|
-
expect(() => index = new Index(indexFile)).to.throwError(/Invalid file header/);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('throws on opening an index file with different version', function() {
|
|
70
|
-
const indexFile = dataDirectory + '/.index';
|
|
71
|
-
fs.writeFileSync(indexFile, 'nesidx00');
|
|
72
|
-
expect(() => index = new Index(indexFile)).to.throwError(/Invalid file version/);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('throws on opening an index file with wrong metadata size', function() {
|
|
76
|
-
const indexFile = dataDirectory + '/.index';
|
|
77
|
-
const metadataBuffer = Buffer.allocUnsafe(8 + 4);
|
|
78
|
-
metadataBuffer.write("nesidx01", 0, 8, 'utf8');
|
|
79
|
-
metadataBuffer.writeUInt32BE(0, 8);
|
|
80
|
-
fs.writeFileSync(indexFile, metadataBuffer);
|
|
81
|
-
|
|
82
|
-
expect(() => index = new Index(indexFile)).to.throwError(/Invalid metadata size/);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('throws on opening an index file with too large metadata size', function() {
|
|
86
|
-
const indexFile = dataDirectory + '/.index';
|
|
87
|
-
const metadataBuffer = Buffer.allocUnsafe(8 + 4 + 3);
|
|
88
|
-
metadataBuffer.write("nesidx01", 0, 8, 'utf8');
|
|
89
|
-
metadataBuffer.writeUInt32BE(255, 8);
|
|
90
|
-
metadataBuffer.write("{}\n", 12, 3, 'utf8');
|
|
91
|
-
fs.writeFileSync(indexFile, metadataBuffer);
|
|
92
|
-
|
|
93
|
-
expect(() => index = new Index(indexFile)).to.throwError(/Invalid index file/);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('throws on opening an index file with invalid metadata', function() {
|
|
97
|
-
const indexFile = dataDirectory + '/.index';
|
|
98
|
-
const metadataBuffer = Buffer.allocUnsafe(8 + 4 + 3);
|
|
99
|
-
metadataBuffer.write("nesidx01", 0, 8, 'utf8');
|
|
100
|
-
metadataBuffer.writeUInt32BE(255, 8);
|
|
101
|
-
metadataBuffer.write("{x$", 12, 3, 'utf8');
|
|
102
|
-
fs.writeFileSync(indexFile, metadataBuffer);
|
|
103
|
-
|
|
104
|
-
expect(() => index = new Index(indexFile)).to.throwError(/Invalid metadata/);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('throws on reopening with altered metadata', function() {
|
|
108
|
-
index = createIndex('.index', { metadata: { test: 'valueStays' } });
|
|
109
|
-
expect(() => index = createIndex(index.name, { metadata: { test: 'anotherValue' } })).to.throwError(/Index metadata mismatch/);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('truncates on opening with altered file', function() {
|
|
113
|
-
index = setupIndexWithEntries(5);
|
|
114
|
-
index.close();
|
|
115
|
-
fs.appendFileSync(index.fileName, 'foo');
|
|
116
|
-
expect(() => index = createIndex(index.name)).to.not.throwError();
|
|
117
|
-
expect(index.length).to.be(5);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
describe('Entry', function() {
|
|
121
|
-
|
|
122
|
-
it('stores data correctly', function() {
|
|
123
|
-
let entry = new Index.Entry(1, 2, 3, 4);
|
|
124
|
-
expect(entry.number).to.be(1);
|
|
125
|
-
expect(entry.position).to.be(2);
|
|
126
|
-
expect(entry.size).to.be(3);
|
|
127
|
-
expect(entry.partition).to.be(4);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('correctly validates custom entry classes', function() {
|
|
131
|
-
class CustomEntryClassWithMissingFromBuffer {
|
|
132
|
-
static get size() { return 4; }
|
|
133
|
-
}
|
|
134
|
-
class CustomEntryClassWithMissingToBuffer {
|
|
135
|
-
static get size() { return 4; }
|
|
136
|
-
static fromBuffer(buffer, offset = 0) {}
|
|
137
|
-
}
|
|
138
|
-
class CustomZeroSizeEntryClass {
|
|
139
|
-
static get size() { return 0; }
|
|
140
|
-
static fromBuffer(buffer, offset = 0) {}
|
|
141
|
-
toBuffer(buffer, offset) {}
|
|
142
|
-
}
|
|
143
|
-
function CustomEs5Entry() {}
|
|
144
|
-
CustomEs5Entry.size = 4;
|
|
145
|
-
CustomEs5Entry.fromBuffer = function(buffer, offset) {};
|
|
146
|
-
CustomEs5Entry.prototype.toBuffer = function(buffer, offset) {};
|
|
147
|
-
expect(() => Index.Entry.assertValidEntryClass({})).to.throwError(/Invalid index entry class/);
|
|
148
|
-
expect(() => Index.Entry.assertValidEntryClass(CustomEntryClassWithMissingFromBuffer)).to.throwError(/Invalid index entry class/);
|
|
149
|
-
expect(() => Index.Entry.assertValidEntryClass(CustomEntryClassWithMissingToBuffer)).to.throwError(/Invalid index entry class/);
|
|
150
|
-
expect(() => Index.Entry.assertValidEntryClass(CustomZeroSizeEntryClass)).to.throwError(/size must be positive/);
|
|
151
|
-
expect(() => Index.Entry.assertValidEntryClass(CustomEs5Entry)).to.not.throwError();
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
describe('add', function() {
|
|
157
|
-
|
|
158
|
-
it('appends entries sequentially', function() {
|
|
159
|
-
index = setupIndexWithEntries(25);
|
|
160
|
-
index.close();
|
|
161
|
-
index.open();
|
|
162
|
-
let entries = index.all();
|
|
163
|
-
expect(entries.length).to.be(25);
|
|
164
|
-
for (let i = 1; i <= entries.length; i++) {
|
|
165
|
-
expect(entries[i - 1].number).to.be(i);
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it('appends entries to reopened index correctly', function() {
|
|
170
|
-
index = setupIndexWithEntries(5);
|
|
171
|
-
index.close();
|
|
172
|
-
index.open();
|
|
173
|
-
index.add(new Index.Entry(6, 6));
|
|
174
|
-
let entries = index.all();
|
|
175
|
-
expect(entries.length).to.be(6);
|
|
176
|
-
for (let i = 1; i <= entries.length; i++) {
|
|
177
|
-
expect(entries[i - 1].number).to.be(i);
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('calls callback eventually', function(done) {
|
|
182
|
-
index = createIndex('.index', { flushDelay: 1 });
|
|
183
|
-
let position = index.add(new Index.Entry(1, 0), (number) => {
|
|
184
|
-
expect(number).to.be(position);
|
|
185
|
-
done();
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it('flushes automatically when writeBuffer full', function() {
|
|
190
|
-
index = setupIndexWithEntries(5, { writeBufferSize: 5 * Index.Entry.size });
|
|
191
|
-
expect(index.flush()).to.be(false);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it('throws with invalid entry object', function() {
|
|
195
|
-
index = createIndex();
|
|
196
|
-
expect(() => index.add([1,2,3,4])).to.throwError(/Wrong entry object/);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('throws with invalid entry size', function() {
|
|
200
|
-
index = createIndex();
|
|
201
|
-
class Entry extends Index.Entry {
|
|
202
|
-
static get size() {
|
|
203
|
-
return 20;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
expect(() => index.add(new Entry(1, 0))).to.throwError(/Invalid entry size/);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
describe('get', function() {
|
|
212
|
-
|
|
213
|
-
it('returns false on out of bounds position', function() {
|
|
214
|
-
index = setupIndexWithEntries(5);
|
|
215
|
-
index.close();
|
|
216
|
-
index.open();
|
|
217
|
-
expect(index.get(0)).to.be(false);
|
|
218
|
-
expect(index.get(index.length+1)).to.be(false);
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
it('returns false on closed index', function() {
|
|
222
|
-
index = setupIndexWithEntries(5);
|
|
223
|
-
index.close();
|
|
224
|
-
expect(index.get(1)).to.be(false);
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
it('can read entry from the end', function() {
|
|
228
|
-
setupIndexWithEntries(5);
|
|
229
|
-
index.close();
|
|
230
|
-
index.open();
|
|
231
|
-
let entry = index.get(-1);
|
|
232
|
-
expect(entry.number).to.be(index.length);
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
it('can random read entries', function() {
|
|
236
|
-
index = setupIndexWithEntries(10);
|
|
237
|
-
index.close();
|
|
238
|
-
index.open();
|
|
239
|
-
let entry = index.get(5);
|
|
240
|
-
expect(entry.number).to.be(5);
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
it('can read entries multiple times', function() {
|
|
244
|
-
index = setupIndexWithEntries(10);
|
|
245
|
-
index.close();
|
|
246
|
-
index.open();
|
|
247
|
-
for (let i = 0; i < 5; i++) {
|
|
248
|
-
let entry = index.get(5);
|
|
249
|
-
expect(entry.number).to.be(5);
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
describe('range', function() {
|
|
256
|
-
|
|
257
|
-
it('returns false on out of bounds range position', function() {
|
|
258
|
-
index = setupIndexWithEntries(50);
|
|
259
|
-
index.close();
|
|
260
|
-
index.open();
|
|
261
|
-
expect(index.range(0)).to.be(false);
|
|
262
|
-
expect(index.range(51, 55)).to.be(false);
|
|
263
|
-
expect(index.range(1, 51)).to.be(false);
|
|
264
|
-
expect(index.range(15, 10)).to.be(false);
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
it('returns false on closed index', function() {
|
|
268
|
-
index = setupIndexWithEntries(5);
|
|
269
|
-
index.close();
|
|
270
|
-
expect(index.range(1)).to.be(false);
|
|
271
|
-
expect(index.range(1,5)).to.be(false);
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
it('can read an arbitrary range of entries', function() {
|
|
275
|
-
index = setupIndexWithEntries(50);
|
|
276
|
-
index.close();
|
|
277
|
-
index.open();
|
|
278
|
-
let entries = index.range(21, 37);
|
|
279
|
-
for (let i = 0; i < entries.length; i++) {
|
|
280
|
-
expect(entries[i].number).to.be(21 + i);
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
it('can read a range of entries from the end', function() {
|
|
285
|
-
index = setupIndexWithEntries(50);
|
|
286
|
-
index.close();
|
|
287
|
-
index.open();
|
|
288
|
-
let entries = index.range(-15);
|
|
289
|
-
expect(entries.length).to.be(15);
|
|
290
|
-
for (let i = 0; i < entries.length; i++) {
|
|
291
|
-
expect(entries[i].number).to.be(36 + i);
|
|
292
|
-
}
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it('can read a range of entries until a distance from the end', function() {
|
|
296
|
-
index = setupIndexWithEntries(50);
|
|
297
|
-
index.close();
|
|
298
|
-
index.open();
|
|
299
|
-
let entries = index.range(1, -15);
|
|
300
|
-
expect(entries.length).to.be(36); // 36 because end is inclusive
|
|
301
|
-
for (let i = 0; i < entries.length; i++) {
|
|
302
|
-
expect(entries[i].number).to.be(1 + i);
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
it('can read a single item range of entries', function() {
|
|
307
|
-
index = setupIndexWithEntries(50);
|
|
308
|
-
index.close();
|
|
309
|
-
index.open();
|
|
310
|
-
let entries = index.range(21, 21);
|
|
311
|
-
expect(entries.length).to.be(1);
|
|
312
|
-
expect(entries[0].number).to.be(21);
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
it('returns false with a non-numeric range', function() {
|
|
316
|
-
index = setupIndexWithEntries(5);
|
|
317
|
-
index.close();
|
|
318
|
-
index.open();
|
|
319
|
-
let entries = index.range('foo');
|
|
320
|
-
expect(entries).to.be(false);
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
describe('lastEntry', function() {
|
|
326
|
-
|
|
327
|
-
it('returns the last entry', function() {
|
|
328
|
-
index = setupIndexWithEntries(5);
|
|
329
|
-
expect(index.lastEntry.number).to.be(5);
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
it('returns false on empty index', function() {
|
|
333
|
-
index = setupIndexWithEntries(0);
|
|
334
|
-
expect(index.lastEntry).to.be(false);
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
describe('find', function() {
|
|
340
|
-
|
|
341
|
-
it('returns 0 if no entry is lower or equal searched number', function() {
|
|
342
|
-
index = setupIndexWithEntries(5, i => 5 + i);
|
|
343
|
-
expect(index.find(index.length)).to.be(0);
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
it('returns last entry if all entries are lower searched number', function() {
|
|
347
|
-
index = setupIndexWithEntries(5);
|
|
348
|
-
expect(index.find(index.length+1)).to.be(index.length);
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
it('returns 0 if all entries are lower searched number with min=true', function() {
|
|
352
|
-
index = setupIndexWithEntries(5);
|
|
353
|
-
expect(index.find(index.length+1, true)).to.be(0);
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
it('returns the entry number on exact match', function() {
|
|
357
|
-
index = setupIndexWithEntries(5);
|
|
358
|
-
for (let i = 1; i <= 5; i++) {
|
|
359
|
-
expect(index.find(i)).to.be(i);
|
|
360
|
-
}
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
it('returns the highest entry number lower than the searched number', function() {
|
|
364
|
-
index = setupIndexWithEntries(50, i => 2*i);
|
|
365
|
-
expect(index.find(25)).to.be(12);
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
it('returns the lowest entry number higher than the searched number with min=true', function() {
|
|
369
|
-
index = setupIndexWithEntries(50, i => 2*i);
|
|
370
|
-
expect(index.find(25, true)).to.be(13);
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
describe('truncate', function() {
|
|
376
|
-
|
|
377
|
-
it('truncates after the given index position', function() {
|
|
378
|
-
index = setupIndexWithEntries(5);
|
|
379
|
-
index.close();
|
|
380
|
-
index.open();
|
|
381
|
-
|
|
382
|
-
index.truncate(2);
|
|
383
|
-
expect(index.length).to.be(2);
|
|
384
|
-
|
|
385
|
-
index.close();
|
|
386
|
-
index.open();
|
|
387
|
-
expect(index.length).to.be(2);
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
it('correctly truncates after unflushed entries', function() {
|
|
391
|
-
index = setupIndexWithEntries(5);
|
|
392
|
-
|
|
393
|
-
index.truncate(2);
|
|
394
|
-
expect(index.length).to.be(2);
|
|
395
|
-
|
|
396
|
-
index.close();
|
|
397
|
-
index.open();
|
|
398
|
-
expect(index.length).to.be(2);
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
it('does not truncate closed index', function() {
|
|
402
|
-
index = setupIndexWithEntries(5);
|
|
403
|
-
index.close();
|
|
404
|
-
|
|
405
|
-
index.truncate(2);
|
|
406
|
-
|
|
407
|
-
index.open();
|
|
408
|
-
expect(index.length).to.be(5);
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
it('does nothing if truncating after index length', function() {
|
|
412
|
-
index = setupIndexWithEntries(5);
|
|
413
|
-
index.close();
|
|
414
|
-
index.open();
|
|
415
|
-
|
|
416
|
-
index.truncate(6);
|
|
417
|
-
expect(index.length).to.be(5);
|
|
418
|
-
|
|
419
|
-
index.close();
|
|
420
|
-
index.open();
|
|
421
|
-
expect(index.length).to.be(5);
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
it('truncates whole index if given negative position', function() {
|
|
425
|
-
index = setupIndexWithEntries(5);
|
|
426
|
-
index.close();
|
|
427
|
-
index.open();
|
|
428
|
-
|
|
429
|
-
index.truncate(-5);
|
|
430
|
-
expect(index.length).to.be(0);
|
|
431
|
-
|
|
432
|
-
index.close();
|
|
433
|
-
index.open();
|
|
434
|
-
expect(index.length).to.be(0);
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
describe('validRange', function(){
|
|
440
|
-
|
|
441
|
-
it('returns false for out of range from positions', function(){
|
|
442
|
-
index = setupIndexWithEntries(5);
|
|
443
|
-
expect(index.validRange(0, 1)).to.be(false);
|
|
444
|
-
expect(index.validRange(-1, 1)).to.be(false);
|
|
445
|
-
expect(index.validRange(index.length + 1, index.length + 2)).to.be(false);
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
it('returns false when from greater until', function(){
|
|
449
|
-
index = setupIndexWithEntries(5);
|
|
450
|
-
expect(index.validRange(2, 1)).to.be(false);
|
|
451
|
-
expect(index.validRange(1, 0)).to.be(false);
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
it('returns false for out of range until positions', function(){
|
|
455
|
-
index = setupIndexWithEntries(5);
|
|
456
|
-
expect(index.validRange(1, -1)).to.be(false);
|
|
457
|
-
expect(index.validRange(1, index.length +1)).to.be(false);
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
it('returns true for valid range positions', function(){
|
|
461
|
-
index = setupIndexWithEntries(5);
|
|
462
|
-
expect(index.validRange(1, 1)).to.be(true);
|
|
463
|
-
expect(index.validRange(1, index.length)).to.be(true);
|
|
464
|
-
expect(index.validRange(index.length, index.length)).to.be(true);
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
describe('destroy', function(){
|
|
470
|
-
|
|
471
|
-
it('completely deletes the file', function(){
|
|
472
|
-
index = setupIndexWithEntries(5);
|
|
473
|
-
const fileName = index.fileName;
|
|
474
|
-
index.destroy();
|
|
475
|
-
expect(fs.existsSync(fileName)).to.be(false);
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
describe('flush', function(){
|
|
481
|
-
|
|
482
|
-
it('returns false on a closed index', function(){
|
|
483
|
-
index = setupIndexWithEntries(1);
|
|
484
|
-
index.close();
|
|
485
|
-
expect(index.flush()).to.be(false);
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
it('returns false if nothing to flush', function(){
|
|
489
|
-
index = setupIndexWithEntries(1);
|
|
490
|
-
index.flush();
|
|
491
|
-
expect(index.flush()).to.be(false);
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
});
|
|
495
|
-
|
|
496
|
-
describe('ReadOnly', function(){
|
|
497
|
-
|
|
498
|
-
it('can be created without explicit name', function(){
|
|
499
|
-
expect(() => {
|
|
500
|
-
index = createIndex('.index');
|
|
501
|
-
let reader = new Index.ReadOnly({ dataDirectory });
|
|
502
|
-
reader.close();
|
|
503
|
-
}).to.not.throwError();
|
|
504
|
-
});
|
|
505
|
-
|
|
506
|
-
it('can be opened and closed multiple times', function(){
|
|
507
|
-
index = createIndex('.index');
|
|
508
|
-
let reader = new Index.ReadOnly({ dataDirectory });
|
|
509
|
-
expect(reader.open()).to.be(false);
|
|
510
|
-
reader.close();
|
|
511
|
-
reader.close();
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
it('throws when opening an empty file', function(){
|
|
515
|
-
index = createIndex('.index');
|
|
516
|
-
index.close();
|
|
517
|
-
fs.truncateSync(index.fileName, 0);
|
|
518
|
-
expect(() => createReader(index.name)).to.throwError(/empty/);
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
it('allows multiple readers for a single index', function(){
|
|
522
|
-
index = setupIndexWithEntries(5);
|
|
523
|
-
|
|
524
|
-
let reader1 = createReader(index.name);
|
|
525
|
-
expect(reader1.isOpen()).to.be(true);
|
|
526
|
-
expect(reader1.length).to.be(index.length);
|
|
527
|
-
expect(reader1.lastEntry.number).to.be(index.lastEntry.number);
|
|
528
|
-
|
|
529
|
-
let reader2 = createReader(index.name);
|
|
530
|
-
expect(reader2.isOpen()).to.be(true);
|
|
531
|
-
expect(reader2.length).to.be(index.length);
|
|
532
|
-
expect(reader2.lastEntry.number).to.be(index.lastEntry.number);
|
|
533
|
-
});
|
|
534
|
-
|
|
535
|
-
it('updates when writer flushes', function(done){
|
|
536
|
-
index = setupIndexWithEntries(5);
|
|
537
|
-
let reader1 = createReader(index.name);
|
|
538
|
-
|
|
539
|
-
reader1.on('append', (prev, next) => {
|
|
540
|
-
expect(prev).to.be(5);
|
|
541
|
-
expect(next).to.be(6);
|
|
542
|
-
expect(reader1.get(next).number).to.be(index.get(next).number);
|
|
543
|
-
done();
|
|
544
|
-
});
|
|
545
|
-
|
|
546
|
-
index.add(new Index.Entry(6, 6));
|
|
547
|
-
index.flush();
|
|
548
|
-
fs.fdatasync(index.fd);
|
|
549
|
-
});
|
|
550
|
-
|
|
551
|
-
it('updates when writer truncates', function(done){
|
|
552
|
-
index = setupIndexWithEntries(5);
|
|
553
|
-
let reader1 = createReader(index.name);
|
|
554
|
-
|
|
555
|
-
reader1.on('truncate', (prev, next) => {
|
|
556
|
-
expect(prev).to.be(5);
|
|
557
|
-
expect(next).to.be(0);
|
|
558
|
-
expect(reader1.length).to.be(0);
|
|
559
|
-
done();
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
index.truncate(0);
|
|
563
|
-
fs.fdatasync(index.fd);
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
it('closes when file renamed', function(done){
|
|
567
|
-
index = setupIndexWithEntries(5);
|
|
568
|
-
index.close();
|
|
569
|
-
let reader = createReader(index.name);
|
|
570
|
-
expect(reader.isOpen()).to.be(true);
|
|
571
|
-
|
|
572
|
-
fs.rename(reader.fileName, reader.fileName + '2', () => {
|
|
573
|
-
setTimeout(() => {
|
|
574
|
-
expect(reader.isOpen()).to.be(false);
|
|
575
|
-
done();
|
|
576
|
-
}, 1);
|
|
577
|
-
});
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
it('does not trigger handler when index closed', function(done){
|
|
581
|
-
index = setupIndexWithEntries(5);
|
|
582
|
-
|
|
583
|
-
let reader = createReader(index.name);
|
|
584
|
-
reader.on('truncate', () => expect(this).to.be(false));
|
|
585
|
-
|
|
586
|
-
index.truncate(0);
|
|
587
|
-
reader.close();
|
|
588
|
-
fs.fdatasync(index.fd, () => done());
|
|
589
|
-
});
|
|
590
|
-
});
|
|
591
|
-
});
|