event-storage 0.8.0 → 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.
- package/README.md +50 -541
- package/package.json +22 -21
- package/src/Clock.js +20 -8
- package/src/EventStore.js +244 -51
- package/src/EventStream.js +169 -16
- package/src/Index/ReadableIndex.js +9 -3
- package/src/Index/WritableIndex.js +10 -4
- package/src/JoinEventStream.js +32 -30
- package/src/Partition/ReadablePartition.js +105 -46
- package/src/Partition/WritablePartition.js +73 -14
- package/src/Storage/ReadableStorage.js +136 -13
- package/src/Storage/WritableStorage.js +169 -24
- package/src/Watcher.js +1 -0
- package/src/WatchesFile.js +6 -5
- package/src/metadataUtil.js +79 -0
- package/src/util.js +71 -70
- 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
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
const expect = require('expect.js');
|
|
2
|
-
const fs = require('fs-extra');
|
|
3
|
-
const JoinEventStream = require('../src/JoinEventStream');
|
|
4
|
-
const EventStore = require('../src/EventStore');
|
|
5
|
-
|
|
6
|
-
describe('JoinEventStream', function() {
|
|
7
|
-
|
|
8
|
-
let stream, eventstore;
|
|
9
|
-
const events = [{ type: 'foo' }, { type: 'bar' }, { type: 'baz' }];
|
|
10
|
-
|
|
11
|
-
before(function (done) {
|
|
12
|
-
fs.emptyDirSync('test/data');
|
|
13
|
-
eventstore = new EventStore({
|
|
14
|
-
storageDirectory: 'test/data'
|
|
15
|
-
});
|
|
16
|
-
eventstore.commit('foo', events[0], () => {
|
|
17
|
-
eventstore.commit('bar', events[1], () => {
|
|
18
|
-
eventstore.commit('foo', events[2], () => {
|
|
19
|
-
done();
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
after(function () {
|
|
26
|
-
eventstore.close();
|
|
27
|
-
eventstore = null;
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('makes the name available', function(){
|
|
31
|
-
stream = new JoinEventStream('foo-bar', ['foo', 'bar'], eventstore);
|
|
32
|
-
expect(stream.name).to.be('foo-bar');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('throws if no name specified in constructor', function(){
|
|
36
|
-
expect(() => new JoinEventStream()).to.throwError();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('throws if no or invalid stream list specified in constructor', function(){
|
|
40
|
-
expect(() => new JoinEventStream('foo-bar', 'foo', eventstore)).to.throwError();
|
|
41
|
-
expect(() => new JoinEventStream('foo-bar', [], eventstore)).to.throwError();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('throws if no EventStore specified in constructor', function(){
|
|
45
|
-
expect(() => new JoinEventStream('foo-bar', ['foo', 'bar'])).to.throwError();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('makes all events accessible as array', function(){
|
|
49
|
-
stream = new JoinEventStream('foo-bar', ['foo', 'bar'], eventstore);
|
|
50
|
-
expect(stream.events).to.eql(events);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('returns all events consistently', function(){
|
|
54
|
-
stream = new JoinEventStream('foo-bar', ['foo', 'bar'], eventstore);
|
|
55
|
-
expect(stream.events).to.eql(stream.events);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('can be iterated with for .. of', function(){
|
|
59
|
-
stream = new JoinEventStream('foo-bar', ['foo', 'bar'], eventstore);
|
|
60
|
-
let i = 0;
|
|
61
|
-
for (let event of stream) {
|
|
62
|
-
expect(event).to.eql(events[i++]);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('is a readable stream', function(done){
|
|
67
|
-
stream = new JoinEventStream('foo-bar', ['foo', 'bar'], eventstore);
|
|
68
|
-
let i = 0;
|
|
69
|
-
stream.on('data', (event) => {
|
|
70
|
-
expect(event).to.eql(events[i++]);
|
|
71
|
-
if (i === events.length) {
|
|
72
|
-
done();
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('can limit events fetched with min and max revision', function(){
|
|
78
|
-
stream = new JoinEventStream('foo-bar', ['foo', 'bar'], eventstore, 0, 1);
|
|
79
|
-
const fetchedEvents = stream.events;
|
|
80
|
-
|
|
81
|
-
expect(fetchedEvents.length).to.be(2);
|
|
82
|
-
expect(fetchedEvents[0]).to.eql(events[0]);
|
|
83
|
-
expect(fetchedEvents[1]).to.eql(events[1]);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('can fetch events from the end only', function(){
|
|
87
|
-
stream = new JoinEventStream('foo-bar', ['foo', 'bar'], eventstore, -2, -1);
|
|
88
|
-
const fetchedEvents = stream.events;
|
|
89
|
-
|
|
90
|
-
expect(fetchedEvents.length).to.be(2);
|
|
91
|
-
expect(fetchedEvents[0]).to.eql(events[1]);
|
|
92
|
-
expect(fetchedEvents[1]).to.eql(events[2]);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('is empty when stream does not exist', function(){
|
|
96
|
-
stream = new JoinEventStream('foo-bar', ['baz'], eventstore);
|
|
97
|
-
expect(stream.events).to.be.eql([]);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
describe('forEach', function(){
|
|
101
|
-
|
|
102
|
-
it('invokes a callback with payload, metadata and stream name', function(){
|
|
103
|
-
stream = new JoinEventStream('foo-bar', ['foo', 'bar'], eventstore);
|
|
104
|
-
let i = 0;
|
|
105
|
-
stream.forEach((event, metadata, stream) => {
|
|
106
|
-
expect(event).to.eql(events[i++]);
|
|
107
|
-
expect(stream).to.be(i === 2 ? 'bar' : 'foo');
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
});
|
package/test/Partition.spec.js
DELETED
|
@@ -1,488 +0,0 @@
|
|
|
1
|
-
const expect = require('expect.js');
|
|
2
|
-
const fs = require('fs-extra');
|
|
3
|
-
const Partition = require('../src/Partition');
|
|
4
|
-
|
|
5
|
-
const dataDirectory = __dirname + '/data';
|
|
6
|
-
|
|
7
|
-
describe('Partition', function() {
|
|
8
|
-
|
|
9
|
-
/** @type {WritablePartition} */
|
|
10
|
-
let partition;
|
|
11
|
-
/** @type {Array<ReadOnlyPartition>} */
|
|
12
|
-
let readers = [];
|
|
13
|
-
|
|
14
|
-
beforeEach(function () {
|
|
15
|
-
fs.emptyDirSync(dataDirectory);
|
|
16
|
-
partition = new Partition('.part', { dataDirectory, readBufferSize: 4*1024 });
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
afterEach(function () {
|
|
20
|
-
if (partition) partition.close();
|
|
21
|
-
for (let reader of readers) reader.close();
|
|
22
|
-
readers = [];
|
|
23
|
-
partition = null;
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* @returns {ReadOnlyPartition}
|
|
28
|
-
*/
|
|
29
|
-
function createReader(options = {}) {
|
|
30
|
-
const reader = new Partition.ReadOnly(partition.name, { ...options, dataDirectory });
|
|
31
|
-
readers[readers.length] = reader;
|
|
32
|
-
return reader;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function fillPartition(num, documentBuilder) {
|
|
36
|
-
let lastPosition;
|
|
37
|
-
for (let i = 1; i <= num; i++) {
|
|
38
|
-
lastPosition = partition.write(documentBuilder && documentBuilder(i) || 'foobar', i);
|
|
39
|
-
}
|
|
40
|
-
partition.flush();
|
|
41
|
-
return lastPosition;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
it('creates the storage directory if it does not exist', function() {
|
|
45
|
-
fs.removeSync(dataDirectory);
|
|
46
|
-
partition = new Partition('.part', { dataDirectory });
|
|
47
|
-
expect(fs.existsSync(dataDirectory)).to.be(true);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('is not opened automatically on construct', function() {
|
|
51
|
-
expect(partition.isOpen()).to.be(false);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('does nothing on reopening', function() {
|
|
55
|
-
partition.open();
|
|
56
|
-
expect(partition.isOpen()).to.be(true);
|
|
57
|
-
expect(() => partition.open()).to.not.throwError();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('throws when not providing partition name', function() {
|
|
61
|
-
expect(() => new Partition()).to.throwError();
|
|
62
|
-
expect(() => new Partition([])).to.throwError();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('throws on opening non-partition file', function() {
|
|
66
|
-
let fd = fs.openSync('test/data/.part', 'w');
|
|
67
|
-
fs.writeSync(fd, 'foobar');
|
|
68
|
-
fs.closeSync(fd);
|
|
69
|
-
|
|
70
|
-
expect(() => partition.open()).to.throwError();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('can open an existing empty file', function() {
|
|
74
|
-
let fd = fs.openSync('test/data/.part', 'w');
|
|
75
|
-
fs.closeSync(fd);
|
|
76
|
-
|
|
77
|
-
expect(partition.open()).to.be(true);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('throws when mismatching header version', function() {
|
|
81
|
-
let fd = fs.openSync('test/data/.part', 'w');
|
|
82
|
-
fs.writeSync(fd, 'nesprt00');
|
|
83
|
-
fs.closeSync(fd);
|
|
84
|
-
|
|
85
|
-
expect(() => partition.open()).to.throwError();
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('can be opened and closed multiple times', function() {
|
|
89
|
-
partition.open();
|
|
90
|
-
expect(partition.open()).to.be(true);
|
|
91
|
-
partition.close();
|
|
92
|
-
partition.close();
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
describe('write', function() {
|
|
96
|
-
|
|
97
|
-
it('throws when partition is not open', function() {
|
|
98
|
-
expect(() => partition.write('foo')).to.throwError();
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('calls callback eventually', function(done) {
|
|
102
|
-
partition.open();
|
|
103
|
-
partition.write('foo', done);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('writes data sequentially', function() {
|
|
107
|
-
partition.open();
|
|
108
|
-
fillPartition(50, i => 'foo-' + i.toString());
|
|
109
|
-
partition.close();
|
|
110
|
-
partition.open();
|
|
111
|
-
let i = 1;
|
|
112
|
-
for (let data of partition.readAll()) {
|
|
113
|
-
expect(data).to.be('foo-' + i.toString());
|
|
114
|
-
i++;
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it('writes utf-8 data correctly', function() {
|
|
119
|
-
partition.open();
|
|
120
|
-
let doc1 = partition.write('foo-üöälß');
|
|
121
|
-
let doc2 = partition.write('bar-日本語');
|
|
122
|
-
partition.close();
|
|
123
|
-
partition.open();
|
|
124
|
-
expect(partition.readFrom(doc1)).to.be('foo-üöälß');
|
|
125
|
-
expect(partition.readFrom(doc2)).to.be('bar-日本語');
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('returns file position of written document', function() {
|
|
129
|
-
partition.open();
|
|
130
|
-
let positions = [];
|
|
131
|
-
for (let i = 1; i <= 10; i++) {
|
|
132
|
-
positions.push(partition.write('foo-' + i.toString()));
|
|
133
|
-
}
|
|
134
|
-
partition.close();
|
|
135
|
-
partition.open();
|
|
136
|
-
for (let i = 1; i <= positions.length; i++) {
|
|
137
|
-
expect(partition.readFrom(positions[i - 1])).to.be('foo-' + i.toString());
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('can write large documents', function(done) {
|
|
142
|
-
partition.open();
|
|
143
|
-
let blob = 'foobar'.repeat(100000);
|
|
144
|
-
partition.write(blob, () => {
|
|
145
|
-
let stat = fs.statSync('test/data/.part');
|
|
146
|
-
expect(stat.size).to.be.greaterThan(blob.length);
|
|
147
|
-
done();
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('works with small write buffer', function() {
|
|
152
|
-
partition = new Partition('.part', { dataDirectory, writeBufferSize: 64 });
|
|
153
|
-
partition.open();
|
|
154
|
-
fillPartition(50, i => 'foo-' + i.toString());
|
|
155
|
-
partition.close();
|
|
156
|
-
partition.open();
|
|
157
|
-
let i = 1;
|
|
158
|
-
for (let data of partition.readAll()) {
|
|
159
|
-
expect(data).to.be('foo-' + i.toString());
|
|
160
|
-
i++;
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
describe('readAll', function() {
|
|
167
|
-
|
|
168
|
-
it('reads all documents in write order', function() {
|
|
169
|
-
partition.open();
|
|
170
|
-
fillPartition(50, i => 'foo-' + i.toString());
|
|
171
|
-
partition.close();
|
|
172
|
-
partition.open();
|
|
173
|
-
let i = 1;
|
|
174
|
-
for (let data of partition.readAll()) {
|
|
175
|
-
expect(data).to.be('foo-' + i.toString());
|
|
176
|
-
i++;
|
|
177
|
-
}
|
|
178
|
-
expect(i).to.be(51);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('reads all documents in write order from arbitrary position', function() {
|
|
182
|
-
partition.open();
|
|
183
|
-
fillPartition(50, i => 'foo-' + i.toString());
|
|
184
|
-
partition.close();
|
|
185
|
-
partition.open();
|
|
186
|
-
let i = 50;
|
|
187
|
-
for (let data of partition.readAll(-partition.documentWriteSize('foo-50'.length)-1)) {
|
|
188
|
-
expect(data).to.be('foo-' + i.toString());
|
|
189
|
-
i++;
|
|
190
|
-
}
|
|
191
|
-
expect(i).to.be(51);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it('reads all documents in backwards write order', function() {
|
|
195
|
-
partition.open();
|
|
196
|
-
fillPartition(50, i => 'foo-' + i.toString());
|
|
197
|
-
partition.close();
|
|
198
|
-
partition.open();
|
|
199
|
-
let i = 50;
|
|
200
|
-
for (let data of partition.readAllBackwards()) {
|
|
201
|
-
expect(data).to.be('foo-' + i.toString());
|
|
202
|
-
i--;
|
|
203
|
-
}
|
|
204
|
-
expect(i).to.be(0);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it('reads all documents in backwards write order from arbitary position', function() {
|
|
208
|
-
partition.open();
|
|
209
|
-
fillPartition(50, i => 'foo-' + i.toString());
|
|
210
|
-
partition.close();
|
|
211
|
-
partition.open();
|
|
212
|
-
let i = 50;
|
|
213
|
-
for (let data of partition.readAllBackwards(-9)) {
|
|
214
|
-
expect(data).to.be('foo-' + i.toString());
|
|
215
|
-
i--;
|
|
216
|
-
}
|
|
217
|
-
expect(i).to.be(0);
|
|
218
|
-
i = 50;
|
|
219
|
-
for (let data of partition.readAllBackwards(partition.size - 12)) {
|
|
220
|
-
expect(data).to.be('foo-' + i.toString());
|
|
221
|
-
i--;
|
|
222
|
-
}
|
|
223
|
-
expect(i).to.be(0);
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
it('can find document boundaries by scanning across readbuffers', function() {
|
|
227
|
-
partition.open();
|
|
228
|
-
const lastPosition = fillPartition(3, () => '0xFF'.repeat(64));
|
|
229
|
-
partition.close();
|
|
230
|
-
|
|
231
|
-
const reader = createReader({ readBufferSize: 64 });
|
|
232
|
-
reader.open();
|
|
233
|
-
expect(reader.findDocumentPositionBefore(reader.size - 8)).to.be(lastPosition);
|
|
234
|
-
reader.close();
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
describe('readFrom', function() {
|
|
240
|
-
|
|
241
|
-
it('throws when partition is not open', function() {
|
|
242
|
-
expect(() => partition.readFrom(0)).to.throwError();
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
it('returns false when reading beyond file size', function() {
|
|
246
|
-
partition.open();
|
|
247
|
-
partition.write('foobar');
|
|
248
|
-
expect(partition.readFrom(5000)).to.be(false);
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
it('can read unflushed documents', function() {
|
|
252
|
-
partition.open();
|
|
253
|
-
let position = partition.write('foobar');
|
|
254
|
-
expect(partition.readFrom(position, 6)).to.be('foobar');
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it('can disable dirty reads', function() {
|
|
258
|
-
partition = new Partition('.part', { dataDirectory, dirtyReads: false });
|
|
259
|
-
partition.open();
|
|
260
|
-
let position = partition.write('foobar');
|
|
261
|
-
expect(partition.readFrom(position, 6)).to.be(false);
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
it('validates document size', function() {
|
|
265
|
-
partition.open();
|
|
266
|
-
fillPartition(10);
|
|
267
|
-
partition.close();
|
|
268
|
-
partition.open();
|
|
269
|
-
|
|
270
|
-
expect(partition.readFrom(0, 6)).to.be('foobar');
|
|
271
|
-
expect(() => partition.readFrom(0, 4)).to.throwError((e) => {
|
|
272
|
-
expect(e).to.be.a(Partition.InvalidDataSizeError);
|
|
273
|
-
});
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
it('throws when reading from an invalid position', function() {
|
|
277
|
-
partition.open();
|
|
278
|
-
fillPartition(10);
|
|
279
|
-
partition.close();
|
|
280
|
-
partition.open();
|
|
281
|
-
|
|
282
|
-
expect(() => console.log(partition.readFrom(3))).to.throwError();
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
it('throws when reading unexpected document size', function() {
|
|
286
|
-
partition.open();
|
|
287
|
-
fillPartition(10);
|
|
288
|
-
partition.close();
|
|
289
|
-
partition.open();
|
|
290
|
-
|
|
291
|
-
expect(() => partition.readFrom(4, 6)).to.throwError();
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
it('throws when an unfinished write is found', function() {
|
|
295
|
-
partition.open();
|
|
296
|
-
const position = partition.write('foobar');
|
|
297
|
-
partition.close();
|
|
298
|
-
|
|
299
|
-
let fd = fs.openSync('test/data/.part', 'r+');
|
|
300
|
-
fs.ftruncateSync(fd, partition.headerSize + position + partition.documentWriteSize('foobar'.length) - 4);
|
|
301
|
-
fs.closeSync(fd);
|
|
302
|
-
|
|
303
|
-
partition.open();
|
|
304
|
-
|
|
305
|
-
expect(() => partition.readFrom(0)).to.throwError((e) => {
|
|
306
|
-
expect(e).to.be.a(Partition.CorruptFileError);
|
|
307
|
-
});
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
it('can read more documents than fit into a single read buffer', function() {
|
|
311
|
-
partition.open();
|
|
312
|
-
fillPartition(1000);
|
|
313
|
-
partition.close();
|
|
314
|
-
partition.open();
|
|
315
|
-
|
|
316
|
-
let pos = 0;
|
|
317
|
-
for (let i = 0; i < 1000; i++) {
|
|
318
|
-
expect(partition.readFrom(pos)).to.be('foobar');
|
|
319
|
-
pos += partition.documentWriteSize('foobar'.length);
|
|
320
|
-
}
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
it('can read large documents', function() {
|
|
324
|
-
partition.open();
|
|
325
|
-
let blob = 'foobar'.repeat(100000);
|
|
326
|
-
partition.write(blob);
|
|
327
|
-
|
|
328
|
-
partition.close();
|
|
329
|
-
partition.open();
|
|
330
|
-
|
|
331
|
-
let read = partition.readFrom(0);
|
|
332
|
-
expect(read).to.be(blob);
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
describe('truncate', function() {
|
|
338
|
-
|
|
339
|
-
it('does nothing if truncating beyond filesize', function() {
|
|
340
|
-
partition.open();
|
|
341
|
-
let lastposition = fillPartition(10);
|
|
342
|
-
let size = partition.size;
|
|
343
|
-
partition.truncate(lastposition + 1000);
|
|
344
|
-
expect(partition.size).to.be(size);
|
|
345
|
-
|
|
346
|
-
partition.close();
|
|
347
|
-
partition.open();
|
|
348
|
-
expect(partition.size).to.be(size);
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
it('truncating whole file if negative position given', function() {
|
|
352
|
-
partition.open();
|
|
353
|
-
fillPartition(10);
|
|
354
|
-
partition.close();
|
|
355
|
-
partition.open();
|
|
356
|
-
|
|
357
|
-
partition.truncate(-3);
|
|
358
|
-
expect(partition.size).to.be(0);
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
it('throws when truncating outside document boundary', function() {
|
|
362
|
-
partition.open();
|
|
363
|
-
let lastposition = fillPartition(10);
|
|
364
|
-
partition.close();
|
|
365
|
-
partition.open();
|
|
366
|
-
|
|
367
|
-
expect(() => partition.truncate(lastposition - 3)).to.throwError();
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
it('truncates after the given position', function() {
|
|
371
|
-
partition.open();
|
|
372
|
-
let lastposition = fillPartition(10);
|
|
373
|
-
partition.close();
|
|
374
|
-
partition.open();
|
|
375
|
-
|
|
376
|
-
partition.truncate(lastposition);
|
|
377
|
-
expect(partition.size).to.be(lastposition);
|
|
378
|
-
|
|
379
|
-
partition.close();
|
|
380
|
-
partition.open();
|
|
381
|
-
expect(partition.size).to.be(lastposition);
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
it('can not read a truncated document', function() {
|
|
385
|
-
partition.open();
|
|
386
|
-
let lastposition = fillPartition(10);
|
|
387
|
-
expect(partition.readFrom(0)).to.not.be(false);
|
|
388
|
-
expect(partition.readFrom(lastposition)).to.not.be(false);
|
|
389
|
-
partition.truncate(lastposition);
|
|
390
|
-
expect(partition.readFrom(lastposition)).to.be(false);
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
it('correctly truncates after unflushed writes', function() {
|
|
394
|
-
partition.open();
|
|
395
|
-
let lastposition = fillPartition(10);
|
|
396
|
-
partition.truncate(lastposition);
|
|
397
|
-
expect(partition.size).to.be(lastposition);
|
|
398
|
-
|
|
399
|
-
partition.close();
|
|
400
|
-
partition.open();
|
|
401
|
-
expect(partition.size).to.be(lastposition);
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
it('correctly truncates torn writes', function() {
|
|
405
|
-
partition.open();
|
|
406
|
-
const position = fillPartition(5);
|
|
407
|
-
partition.close();
|
|
408
|
-
|
|
409
|
-
let fd = fs.openSync('test/data/.part', 'r+');
|
|
410
|
-
fs.ftruncateSync(fd, partition.headerSize + position + Partition.DOCUMENT_HEADER_SIZE + 4);
|
|
411
|
-
fs.closeSync(fd);
|
|
412
|
-
|
|
413
|
-
partition.open();
|
|
414
|
-
|
|
415
|
-
partition.truncate(position);
|
|
416
|
-
expect(partition.size).to.be(position);
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
describe('concurrency', function(){
|
|
422
|
-
|
|
423
|
-
it('allows multiple readers for a partition', function(){
|
|
424
|
-
partition.open();
|
|
425
|
-
fillPartition(10);
|
|
426
|
-
expect(partition.size).to.be.greaterThan(0);
|
|
427
|
-
|
|
428
|
-
let reader1 = createReader();
|
|
429
|
-
reader1.open();
|
|
430
|
-
expect(reader1.size).to.be(partition.size);
|
|
431
|
-
let reader2 = createReader();
|
|
432
|
-
reader2.open();
|
|
433
|
-
expect(reader2.size).to.be(partition.size);
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
it('updates reader when writer appends', function(done){
|
|
437
|
-
partition.open();
|
|
438
|
-
fillPartition(10);
|
|
439
|
-
const size = partition.size;
|
|
440
|
-
|
|
441
|
-
let reader = createReader();
|
|
442
|
-
reader.open();
|
|
443
|
-
reader.on('append', (prevSize, newSize) => {
|
|
444
|
-
expect(prevSize).to.be(size);
|
|
445
|
-
expect(newSize).to.be(partition.size);
|
|
446
|
-
done();
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
partition.write('foo');
|
|
450
|
-
expect(partition.size).to.be.greaterThan(reader.size);
|
|
451
|
-
partition.flush();
|
|
452
|
-
fs.fdatasync(partition.fd);
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
it('updates reader when writer truncates', function(done){
|
|
456
|
-
partition.open();
|
|
457
|
-
fillPartition(10);
|
|
458
|
-
const size = partition.size;
|
|
459
|
-
|
|
460
|
-
let reader = createReader();
|
|
461
|
-
reader.open();
|
|
462
|
-
reader.on('truncate', (prevSize, newSize) => {
|
|
463
|
-
expect(prevSize).to.be(size);
|
|
464
|
-
expect(newSize).to.be(partition.size);
|
|
465
|
-
reader.close();
|
|
466
|
-
done();
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
partition.truncate(0);
|
|
470
|
-
fs.fdatasync(partition.fd);
|
|
471
|
-
});
|
|
472
|
-
|
|
473
|
-
it('recognizes file renames and closes', function(done){
|
|
474
|
-
partition.open();
|
|
475
|
-
fillPartition(10);
|
|
476
|
-
partition.close();
|
|
477
|
-
|
|
478
|
-
let reader = createReader();
|
|
479
|
-
reader.open();
|
|
480
|
-
fs.rename(reader.fileName, reader.fileName + '2', () => {
|
|
481
|
-
setTimeout(() => {
|
|
482
|
-
expect(reader.isOpen()).to.be(false);
|
|
483
|
-
done();
|
|
484
|
-
}, 5);
|
|
485
|
-
});
|
|
486
|
-
});
|
|
487
|
-
});
|
|
488
|
-
});
|