handle eventAppeared callback returning Promise

This commit is contained in:
Nicolas Dextraze 2017-07-16 17:11:54 -07:00
parent 0b968c40e8
commit 7c94e26055
12 changed files with 269 additions and 102 deletions

2
index.d.ts vendored
View File

@ -212,7 +212,7 @@ export interface PersistentSubscriptionDeleteResult {
// Callbacks // Callbacks
export interface EventAppearedCallback<TSubscription> { export interface EventAppearedCallback<TSubscription> {
(subscription: TSubscription, event: ResolvedEvent): void; (subscription: TSubscription, event: ResolvedEvent): void | Promise<void>;
} }
export interface LiveProcessingStartedCallback { export interface LiveProcessingStartedCallback {

View File

@ -1,6 +1,6 @@
{ {
"name": "node-eventstore-client", "name": "node-eventstore-client",
"version": "0.1.5", "version": "0.1.7",
"description": "A port of the EventStore .Net ClientAPI to Node.js", "description": "A port of the EventStore .Net ClientAPI to Node.js",
"main": "index.js", "main": "index.js",
"types": "index.d.ts", "types": "index.d.ts",
@ -49,8 +49,8 @@
"uuid-parse": "^1.0.0" "uuid-parse": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"jsdoc": "^3.4.2", "jsdoc": "^3.5.3",
"nodeunit": "^0.11.0", "nodeunit": "^0.11.1",
"webpack": "^2.4.1" "webpack": "^3.3.0"
} }
} }

View File

@ -232,7 +232,7 @@ SubscriptionOperation.prototype._onEventAppeared = function(e) {
e.originalStreamId, e.originalEventNumber, e.originalEvent.eventType, e.originalPosition); e.originalStreamId, e.originalEventNumber, e.originalEvent.eventType, e.originalPosition);
var self = this; var self = this;
this._executeAction(function() { self._eventAppeared(self._subscription, e); }); this._executeAction(function() { return self._eventAppeared(self._subscription, e); });
}; };
SubscriptionOperation.prototype._executeAction = function(action) { SubscriptionOperation.prototype._executeAction = function(action) {
@ -249,15 +249,25 @@ SubscriptionOperation.prototype._executeActions = function() {
this._actionExecuting = false; this._actionExecuting = false;
return; return;
} }
var promise;
try try
{ {
action(); promise = action();
} }
catch (err) catch (err)
{ {
this._log.error(err, "Exception during executing user callback: %s.", err.message); this._log.error(err, "Exception during executing user callback: %s.", err.message);
} }
if (promise && promise.then) {
var self = this;
promise
.catch(function (err) {
self._log.error(err, "Exception during executing user callback: %s.", err.message);
})
.then(this._executeActions.bind(this));
} else {
setImmediate(this._executeActions.bind(this)); setImmediate(this._executeActions.bind(this));
}
}; };
SubscriptionOperation.prototype.toString = function() { SubscriptionOperation.prototype.toString = function() {

View File

@ -24,14 +24,10 @@ EventStoreAllCatchUpSubscription.prototype._readEventsTill = function(
var self = this; var self = this;
function processEvents(events, index) { function processEvents(events, index) {
index = index || 0;
if (index >= events.length) return Promise.resolve(); if (index >= events.length) return Promise.resolve();
if (events[index].originalPosition === null) throw new Error("Subscription event came up with no OriginalPosition."); if (events[index].originalPosition === null) throw new Error("Subscription event came up with no OriginalPosition.");
return new Promise(function(resolve, reject) { return self._tryProcess(events[index])
self._tryProcess(events[index]);
resolve();
})
.then(function() { .then(function() {
return processEvents(events, index + 1); return processEvents(events, index + 1);
}); });
@ -40,13 +36,12 @@ EventStoreAllCatchUpSubscription.prototype._readEventsTill = function(
function readNext() { function readNext() {
return connection.readAllEventsForward(self._nextReadPosition, self.readBatchSize, resolveLinkTos, userCredentials) return connection.readAllEventsForward(self._nextReadPosition, self.readBatchSize, resolveLinkTos, userCredentials)
.then(function(slice) { .then(function(slice) {
return processEvents(slice.events) return processEvents(slice.events, 0)
.then(function() { .then(function() {
self._nextReadPosition = slice.nextPosition; self._nextReadPosition = slice.nextPosition;
var done = lastCommitPosition === null return (lastCommitPosition === null)
? slice.isEndOfStream ? slice.isEndOfStream
: slice.nextPosition.compareTo(new results.Position(lastCommitPosition, lastCommitPosition)) >= 0; : slice.nextPosition.compareTo(new results.Position(lastCommitPosition, lastCommitPosition)) >= 0;
return Promise.resolve(done);
}); });
}) })
.then(function(done) { .then(function(done) {
@ -67,9 +62,10 @@ EventStoreAllCatchUpSubscription.prototype._readEventsTill = function(
EventStoreAllCatchUpSubscription.prototype._tryProcess = function(e) { EventStoreAllCatchUpSubscription.prototype._tryProcess = function(e) {
var processed = false; var processed = false;
var promise;
if (e.originalPosition.compareTo(this._lastProcessedPosition) > 0) if (e.originalPosition.compareTo(this._lastProcessedPosition) > 0)
{ {
this._eventAppeared(this, e); promise = this._eventAppeared(this, e);
this._lastProcessedPosition = e.originalPosition; this._lastProcessedPosition = e.originalPosition;
processed = true; processed = true;
} }
@ -77,6 +73,7 @@ EventStoreAllCatchUpSubscription.prototype._tryProcess = function(e) {
this._log.debug("Catch-up Subscription to %s: %s event (%s, %d, %s @ %s).", this._log.debug("Catch-up Subscription to %s: %s event (%s, %d, %s @ %s).",
this.streamId || '<all>', processed ? "processed" : "skipping", this.streamId || '<all>', processed ? "processed" : "skipping",
e.originalEvent.eventStreamId, e.originalEvent.eventNumber, e.originalEvent.eventType, e.originalPosition); e.originalEvent.eventStreamId, e.originalEvent.eventNumber, e.originalEvent.eventType, e.originalPosition);
return (promise && promise.then) ? promise : Promise.resolve();
}; };
module.exports = EventStoreAllCatchUpSubscription; module.exports = EventStoreAllCatchUpSubscription;

View File

@ -219,15 +219,25 @@ EventStoreCatchUpSubscription.prototype._processLiveQueue = function() {
this._isProcessing = false; this._isProcessing = false;
return; return;
} }
var promise;
try { try {
this._tryProcess(ev); promise = this._tryProcess(ev);
} }
catch(err) { catch(err) {
this._dropSubscription(SubscriptionDropReason.EventHandlerException, err); this._dropSubscription(SubscriptionDropReason.EventHandlerException, err);
this._isProcessing = false; this._isProcessing = false;
return; return;
} }
if (promise && promise.then) {
var self = this;
promise
.then(this._processLiveQueue.bind(this), function(err) {
self._dropSubscription(SubscriptionDropReason.EventHandlerException, err);
self._isProcessing = false;
});
} else {
setImmediate(this._processLiveQueue.bind(this)); setImmediate(this._processLiveQueue.bind(this));
}
}; };
EventStoreCatchUpSubscription.prototype._dropSubscription = function(reason, error) { EventStoreCatchUpSubscription.prototype._dropSubscription = function(reason, error) {

View File

@ -112,6 +112,14 @@ EventStorePersistentSubscriptionBase.prototype._enqueue = function(resolvedEvent
} }
}; };
function runAsync(fn) {
try {
return Promise.resolve(fn());
} catch(e) {
return Promise.reject(e);
}
}
EventStorePersistentSubscriptionBase.prototype._processQueue = function() { EventStorePersistentSubscriptionBase.prototype._processQueue = function() {
var ev = this._queue.shift(); var ev = this._queue.shift();
if (!ev) { if (!ev) {
@ -132,24 +140,28 @@ EventStorePersistentSubscriptionBase.prototype._processQueue = function() {
this._isProcessing = false; this._isProcessing = false;
return; return;
} }
try var self = this;
{ runAsync(function() {
this._eventAppeared(this, ev); return self._eventAppeared(self, ev);
if(this._autoAck) })
this._subscription.notifyEventsProcessed([ev.originalEvent.eventId]); .then(function() {
if (this._verbose) if(self._autoAck)
this._log.debug("Persistent Subscription to %s: processed event (%s, %d, %s @ %d).", self._subscription.notifyEventsProcessed([ev.originalEvent.eventId]);
this._streamId, ev.originalEvent.eventStreamId, ev.originalEvent.eventNumber, ev.originalEvent.eventType, if (self._verbose)
self._log.debug("Persistent Subscription to %s: processed event (%s, %d, %s @ %d).",
self._streamId, ev.originalEvent.eventStreamId, ev.originalEvent.eventNumber, ev.originalEvent.eventType,
ev.originalEventNumber); ev.originalEventNumber);
} return false;
catch (err) }, function(err) {
{
//TODO GFY should we autonak here? //TODO GFY should we autonak here?
this._dropSubscription(SubscriptionDropReason.EventHandlerException, err); self._dropSubscription(SubscriptionDropReason.EventHandlerException, err);
this._isProcessing = false; self._isProcessing = false;
return; return true;
} })
setImmediate(this._processQueue.bind(this)); .then(function (faulted) {
if (faulted) return;
self._processQueue();
});
}; };
EventStorePersistentSubscriptionBase.prototype._dropSubscription = function(reason, error) { EventStorePersistentSubscriptionBase.prototype._dropSubscription = function(reason, error) {
@ -162,8 +174,13 @@ EventStorePersistentSubscriptionBase.prototype._dropSubscription = function(reas
if (this._subscription !== null) if (this._subscription !== null)
this._subscription.unsubscribe(); this._subscription.unsubscribe();
if (this._subscriptionDropped !== null) if (this._subscriptionDropped !== null) {
try {
this._subscriptionDropped(this, reason, error); this._subscriptionDropped(this, reason, error);
} catch (e) {
this._log.error(e, "Persistent Subscription to %s: subscriptionDropped callback failed.", this._streamId);
}
}
this._stopped = true; this._stopped = true;
} }
}; };

View File

@ -19,19 +19,21 @@ function EventStoreStreamCatchUpSubscription(
} }
util.inherits(EventStoreStreamCatchUpSubscription, EventStoreCatchUpSubscription); util.inherits(EventStoreStreamCatchUpSubscription, EventStoreCatchUpSubscription);
function delay(ms, result) {
return new Promise(function (resolve, reject) {
setTimeout(resolve, ms, result);
})
}
EventStoreStreamCatchUpSubscription.prototype._readEventsTill = function( EventStoreStreamCatchUpSubscription.prototype._readEventsTill = function(
connection, resolveLinkTos, userCredentials, lastCommitPosition, lastEventNumber connection, resolveLinkTos, userCredentials, lastCommitPosition, lastEventNumber
) { ) {
var self = this; var self = this;
function processEvents(events, index) { function processEvents(events, index) {
index = index || 0;
if (index >= events.length) return Promise.resolve(); if (index >= events.length) return Promise.resolve();
return new Promise(function(resolve, reject) { return self._tryProcess(events[index])
self._tryProcess(events[index]);
resolve();
})
.then(function() { .then(function() {
return processEvents(events, index + 1); return processEvents(events, index + 1);
}); });
@ -42,12 +44,12 @@ EventStoreStreamCatchUpSubscription.prototype._readEventsTill = function(
.then(function(slice) { .then(function(slice) {
switch(slice.status) { switch(slice.status) {
case SliceReadStatus.Success: case SliceReadStatus.Success:
return processEvents(slice.events) return processEvents(slice.events, 0)
.then(function() { .then(function() {
self._nextReadEventNumber = slice.nextEventNumber; self._nextReadEventNumber = slice.nextEventNumber;
var done = Promise.resolve(lastEventNumber === null ? slice.isEndOfStream : slice.nextEventNumber > lastEventNumber); var done = Promise.resolve(lastEventNumber === null ? slice.isEndOfStream : slice.nextEventNumber > lastEventNumber);
if (!done && slice.isEndOfStream) if (!done && slice.isEndOfStream)
return done.delay(10); return delay(100, false);
return done; return done;
}); });
break; break;
@ -77,8 +79,9 @@ EventStoreStreamCatchUpSubscription.prototype._readEventsTill = function(
EventStoreStreamCatchUpSubscription.prototype._tryProcess = function(e) { EventStoreStreamCatchUpSubscription.prototype._tryProcess = function(e) {
var processed = false; var processed = false;
var promise;
if (e.originalEventNumber > this._lastProcessedEventNumber) { if (e.originalEventNumber > this._lastProcessedEventNumber) {
this._eventAppeared(this, e); promise = this._eventAppeared(this, e);
this._lastProcessedEventNumber = e.originalEventNumber; this._lastProcessedEventNumber = e.originalEventNumber;
processed = true; processed = true;
} }
@ -86,6 +89,7 @@ EventStoreStreamCatchUpSubscription.prototype._tryProcess = function(e) {
this._log.debug("Catch-up Subscription to %s: %s event (%s, %d, %s @ %d).", this._log.debug("Catch-up Subscription to %s: %s event (%s, %d, %s @ %d).",
this.isSubscribedToAll ? '<all>' : this.streamId, processed ? "processed" : "skipping", this.isSubscribedToAll ? '<all>' : this.streamId, processed ? "processed" : "skipping",
e.originalEvent.eventStreamId, e.originalEvent.eventNumber, e.originalEvent.eventType, e.originalEventNumber) e.originalEvent.eventStreamId, e.originalEvent.eventNumber, e.originalEvent.eventType, e.originalEventNumber)
return (promise && promise.then) ? promise : Promise.resolve();
}; };

View File

@ -9,6 +9,20 @@ function createRandomEvent() {
var testStreamName = 'test-' + uuid.v4(); var testStreamName = 'test-' + uuid.v4();
function delay(ms) {
return new Promise(function (resolve, reject) {
setTimeout(resolve, ms);
})
}
function delayOnlyFirst(count, action) {
if (count === 0) return action();
return delay(200)
.then(function () {
action();
})
}
module.exports = { module.exports = {
'Test Create Persistent Subscription': function(test) { 'Test Create Persistent Subscription': function(test) {
var settings = client.PersistentSubscriptionSettings.create(); var settings = client.PersistentSubscriptionSettings.create();
@ -22,7 +36,8 @@ module.exports = {
}, },
//TODO: Update Persistent Subscription //TODO: Update Persistent Subscription
'Test ConnectTo Persistent Subscription': function(test) { 'Test ConnectTo Persistent Subscription': function(test) {
test.expect(3); test.expect(4);
var receivedEvents = [];
var _doneCount = 0; var _doneCount = 0;
function done(err) { function done(err) {
test.ok(!err, err ? err.stack : ''); test.ok(!err, err ? err.stack : '');
@ -31,16 +46,21 @@ module.exports = {
test.done(); test.done();
} }
function eventAppeared(s, e) { function eventAppeared(s, e) {
s.stop(); return delayOnlyFirst(receivedEvents.length, function () {
receivedEvents.push(e);
if (receivedEvents.length === 2) s.stop();
});
} }
function subscriptionDropped(connection, reason, error) { function subscriptionDropped(connection, reason, error) {
done(error); if (error) return done(error);
test.ok(receivedEvents[1].originalEventNumber > receivedEvents[0].originalEventNumber, "Received events are out of order.");
done();
} }
var self = this; var self = this;
this.conn.connectToPersistentSubscription(testStreamName, 'consumer-1', eventAppeared, subscriptionDropped) this.conn.connectToPersistentSubscription(testStreamName, 'consumer-1', eventAppeared, subscriptionDropped)
.then(function(subscription) { .then(function(subscription) {
test.ok(subscription, "Subscription is null."); test.ok(subscription, "Subscription is null.");
return self.conn.appendToStream(testStreamName, client.expectedVersion.any, [createRandomEvent()]); return self.conn.appendToStream(testStreamName, client.expectedVersion.any, [createRandomEvent(), createRandomEvent()]);
}) })
.then(function () { .then(function () {
done(); done();

View File

@ -7,9 +7,23 @@ function createRandomEvent() {
return client.createJsonEventData(uuid.v4(), {a: uuid.v4(), b: Math.random()}, {createdAt: Date.now()}, 'testEvent'); return client.createJsonEventData(uuid.v4(), {a: uuid.v4(), b: Math.random()}, {createdAt: Date.now()}, 'testEvent');
} }
function delay(ms) {
return new Promise(function (resolve, reject) {
setTimeout(resolve, ms);
})
}
function delayOnlyFirst(count, action) {
if (count === 0) return action();
return delay(200)
.then(function () {
action();
})
}
module.exports = { module.exports = {
'Test Subscribe to All From (Start)': function(test) { 'Test Subscribe to All From (Start)': function(test) {
test.expect(4); test.expect(6);
var self = this; var self = this;
var liveProcessing = false; var liveProcessing = false;
var catchUpEvents = []; var catchUpEvents = [];
@ -19,19 +33,33 @@ module.exports = {
test.ok(!err, err ? err.stack : ''); test.ok(!err, err ? err.stack : '');
_doneCount++; _doneCount++;
if (_doneCount < 2) return; if (_doneCount < 2) return;
var catchUpInOrder = true;
for(var i = 1; i < catchUpEvents.length; i++)
catchUpInOrder = catchUpInOrder && (catchUpEvents[i].originalPosition.compareTo(catchUpEvents[i-1].originalPosition) > 0);
test.ok(catchUpInOrder, "Catch-up events are out of order.");
var liveInOrder = true;
for(var j = 1; j < liveEvents.length; j++)
liveInOrder = liveInOrder && (liveEvents[j].originalPosition.compareTo(liveEvents[j-1].originalPosition) > 0);
test.ok(liveInOrder, "Live events are out of order.");
test.done(); test.done();
} }
function eventAppeared(s, e) { function eventAppeared(s, e) {
if (liveProcessing) { var isLive = liveProcessing;
delayOnlyFirst(isLive ? liveEvents.length : catchUpEvents.length, function() {
if (isLive) {
liveEvents.push(e); liveEvents.push(e);
s.stop();
} else { } else {
catchUpEvents.push(e); catchUpEvents.push(e);
} }
if (isLive && liveEvents.length === 2) s.stop();
});
} }
function liveProcessingStarted() { function liveProcessingStarted() {
liveProcessing = true; liveProcessing = true;
var events = [createRandomEvent()]; var events = [createRandomEvent(), createRandomEvent()];
self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, events) self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, events)
.then(function () { .then(function () {
done(); done();
@ -39,14 +67,14 @@ module.exports = {
.catch(done); .catch(done);
} }
function subscriptionDropped(connection, reason, error) { function subscriptionDropped(connection, reason, error) {
test.ok(liveEvents.length === 1, "Expecting 1 live event, got " + liveEvents.length); test.ok(liveEvents.length === 2, "Expecting 2 live event, got " + liveEvents.length);
test.ok(catchUpEvents.length > 1, "Expecting at least 1 catchUp event, got " + catchUpEvents.length); test.ok(catchUpEvents.length > 1, "Expecting at least 1 catchUp event, got " + catchUpEvents.length);
done(error); done(error);
} }
var subscription = this.conn.subscribeToAllFrom(null, false, eventAppeared, liveProcessingStarted, subscriptionDropped, allCredentials); var subscription = this.conn.subscribeToAllFrom(null, false, eventAppeared, liveProcessingStarted, subscriptionDropped, allCredentials);
}, },
'Test Subscribe to All From (Position)': function(test) { 'Test Subscribe to All From (Position)': function(test) {
test.expect(5); test.expect(7);
var self = this; var self = this;
var liveProcessing = false; var liveProcessing = false;
var catchUpEvents = []; var catchUpEvents = [];
@ -56,19 +84,33 @@ module.exports = {
test.ok(!err, err ? err.stack : ''); test.ok(!err, err ? err.stack : '');
_doneCount++; _doneCount++;
if (_doneCount < 2) return; if (_doneCount < 2) return;
var catchUpInOrder = true;
for(var i = 1; i < catchUpEvents.length; i++)
catchUpInOrder = catchUpInOrder && (catchUpEvents[i].originalPosition.compareTo(catchUpEvents[i-1].originalPosition) > 0);
test.ok(catchUpInOrder, "Catch-up events are out of order.");
var liveInOrder = true;
for(var j = 1; j < liveEvents.length; j++)
liveInOrder = liveInOrder && (liveEvents[j].originalPosition.compareTo(liveEvents[j-1].originalPosition) > 0);
test.ok(liveInOrder, "Live events are out of order.");
test.done(); test.done();
} }
function eventAppeared(s, e) { function eventAppeared(s, e) {
if (liveProcessing) { var isLive = liveProcessing;
delayOnlyFirst(isLive ? liveEvents.length : catchUpEvents.length, function() {
if (isLive) {
liveEvents.push(e); liveEvents.push(e);
s.stop();
} else { } else {
catchUpEvents.push(e); catchUpEvents.push(e);
} }
if (isLive && liveEvents.length === 2) s.stop();
});
} }
function liveProcessingStarted() { function liveProcessingStarted() {
liveProcessing = true; liveProcessing = true;
var events = [createRandomEvent()]; var events = [createRandomEvent(), createRandomEvent()];
self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, events) self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, events)
.then(function () { .then(function () {
done(); done();
@ -76,7 +118,7 @@ module.exports = {
.catch(done); .catch(done);
} }
function subscriptionDropped(connection, reason, error) { function subscriptionDropped(connection, reason, error) {
test.ok(liveEvents.length === 1, "Expecting 1 live event, got " + liveEvents.length); test.ok(liveEvents.length === 2, "Expecting 2 live event, got " + liveEvents.length);
test.ok(catchUpEvents.length > 1, "Expecting at least 1 catchUp event, got " + catchUpEvents.length); test.ok(catchUpEvents.length > 1, "Expecting at least 1 catchUp event, got " + catchUpEvents.length);
done(error); done(error);
} }
@ -87,7 +129,7 @@ module.exports = {
}); });
}, },
'Test Subscribe to All From (End)': function(test) { 'Test Subscribe to All From (End)': function(test) {
test.expect(5); test.expect(6);
var self = this; var self = this;
var liveProcessing = false; var liveProcessing = false;
var catchUpEvents = []; var catchUpEvents = [];
@ -97,19 +139,28 @@ module.exports = {
test.ok(!err, err ? err.stack : ''); test.ok(!err, err ? err.stack : '');
_doneCount++; _doneCount++;
if (_doneCount < 2) return; if (_doneCount < 2) return;
var liveInOrder = true;
for(var j = 1; j < liveEvents.length; j++)
liveInOrder = liveInOrder && (liveEvents[j].originalPosition.compareTo(liveEvents[j-1].originalPosition) > 0);
test.ok(liveInOrder, "Live events are out of order.");
test.done(); test.done();
} }
function eventAppeared(s, e) { function eventAppeared(s, e) {
if (liveProcessing) { var isLive = liveProcessing;
delayOnlyFirst(isLive ? liveEvents.length : catchUpEvents.length, function() {
if (isLive) {
liveEvents.push(e); liveEvents.push(e);
s.stop();
} else { } else {
catchUpEvents.push(e); catchUpEvents.push(e);
} }
if (isLive && liveEvents.length === 2) s.stop();
});
} }
function liveProcessingStarted() { function liveProcessingStarted() {
liveProcessing = true; liveProcessing = true;
var events = [createRandomEvent()]; var events = [createRandomEvent(), createRandomEvent()];
self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, events) self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, events)
.then(function () { .then(function () {
done(); done();
@ -117,7 +168,7 @@ module.exports = {
.catch(done); .catch(done);
} }
function subscriptionDropped(connection, reason, error) { function subscriptionDropped(connection, reason, error) {
test.ok(liveEvents.length === 1, "Expecting 1 live event, got " + liveEvents.length); test.ok(liveEvents.length === 2, "Expecting 2 live event, got " + liveEvents.length);
test.ok(catchUpEvents.length === 0, "Expecting 0 catchUp event, got " + catchUpEvents.length); test.ok(catchUpEvents.length === 0, "Expecting 0 catchUp event, got " + catchUpEvents.length);
done(error); done(error);
} }

View File

@ -2,6 +2,20 @@ const uuid = require('uuid');
const client = require('../src/client'); const client = require('../src/client');
const allCredentials = new client.UserCredentials("admin", "changeit"); const allCredentials = new client.UserCredentials("admin", "changeit");
function delay(ms) {
return new Promise(function (resolve, reject) {
setTimeout(resolve, ms);
})
}
function delayOnlyFirst(count, action) {
if (count === 0) return action();
return delay(200)
.then(function () {
action();
})
}
module.exports = { module.exports = {
'Test Subscribe To All Happy Path': function(test) { 'Test Subscribe To All Happy Path': function(test) {
const resolveLinkTos = false; const resolveLinkTos = false;
@ -30,8 +44,10 @@ module.exports = {
var receivedEvents = []; var receivedEvents = [];
function eventAppeared(subscription, event) { function eventAppeared(subscription, event) {
delayOnlyFirst(receivedEvents.length, function() {
receivedEvents.push(event); receivedEvents.push(event);
if (receivedEvents.length === numberOfPublishedEvents) subscription.close(); if (receivedEvents.length === numberOfPublishedEvents) subscription.close();
});
} }
function subscriptionDropped(subscription, reason, error) { function subscriptionDropped(subscription, reason, error) {
if (error) return done(error); if (error) return done(error);

View File

@ -6,9 +6,23 @@ function createRandomEvent() {
return client.createJsonEventData(uuid.v4(), {a: uuid.v4(), b: Math.random()}, {createdAt: Date.now()}, 'testEvent'); return client.createJsonEventData(uuid.v4(), {a: uuid.v4(), b: Math.random()}, {createdAt: Date.now()}, 'testEvent');
} }
function delay(ms) {
return new Promise(function (resolve, reject) {
setTimeout(resolve, ms);
})
}
function delayOnlyFirst(count, action) {
if (count === 0) return action();
return delay(200)
.then(function () {
action();
})
}
module.exports = { module.exports = {
'Test Subscribe to Stream From Beginning (null)': function(test) { 'Test Subscribe to Stream From Beginning (null)': function(test) {
test.expect(22); test.expect(36);
var self = this; var self = this;
var liveProcessing = false; var liveProcessing = false;
var catchUpEvents = []; var catchUpEvents = [];
@ -22,16 +36,19 @@ module.exports = {
} }
function eventAppeared(s, e) { function eventAppeared(s, e) {
if (liveProcessing) { var isLive = liveProcessing;
delayOnlyFirst(isLive ? liveEvents.length : catchUpEvents.length, function() {
if (isLive) {
liveEvents.push(e); liveEvents.push(e);
s.stop();
} else { } else {
catchUpEvents.push(e); catchUpEvents.push(e);
} }
if (isLive && liveEvents.length === 2) s.stop();
});
} }
function liveProcessingStarted() { function liveProcessingStarted() {
liveProcessing = true; liveProcessing = true;
var events = [createRandomEvent()]; var events = [createRandomEvent(), createRandomEvent()];
self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, events) self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, events)
.then(function () { .then(function () {
done(); done();
@ -39,16 +56,20 @@ module.exports = {
.catch(done); .catch(done);
} }
function subscriptionDropped(connection, reason, error) { function subscriptionDropped(connection, reason, error) {
test.ok(liveEvents.length === 1, "Expecting 1 live event, got " + liveEvents.length); test.ok(liveEvents.length === 2, "Expecting 2 live event, got " + liveEvents.length);
test.testLiveEvent('liveEvents[0]', liveEvents[0]); test.testLiveEvent('liveEvents[0]', liveEvents[0]);
test.ok(liveEvents[0].originalEventNumber, 1); test.testLiveEvent('liveEvents[1]', liveEvents[1]);
test.ok(catchUpEvents.length === 1, "Expecting 1 catchUp event, got " + catchUpEvents.length); test.ok(liveEvents[0].originalEventNumber, 2);
test.ok(liveEvents[1].originalEventNumber, 3);
test.ok(catchUpEvents.length === 2, "Expecting 2 catchUp event, got " + catchUpEvents.length);
test.testReadEvent('catchUpEvents[0]', catchUpEvents[0]); test.testReadEvent('catchUpEvents[0]', catchUpEvents[0]);
test.testReadEvent('catchUpEvents[1]', catchUpEvents[1]);
test.ok(liveEvents[0].originalEventNumber, 0); test.ok(liveEvents[0].originalEventNumber, 0);
test.ok(liveEvents[1].originalEventNumber, 1);
done(error); done(error);
} }
var events = [createRandomEvent()]; var events = [createRandomEvent(), createRandomEvent()];
this.conn.appendToStream(self.testStreamName, client.expectedVersion.noStream, events) this.conn.appendToStream(self.testStreamName, client.expectedVersion.noStream, events)
.then(function() { .then(function() {
var subscription = self.conn.subscribeToStreamFrom(self.testStreamName, null, false, eventAppeared, liveProcessingStarted, subscriptionDropped); var subscription = self.conn.subscribeToStreamFrom(self.testStreamName, null, false, eventAppeared, liveProcessingStarted, subscriptionDropped);
@ -61,7 +82,7 @@ module.exports = {
.catch(test.done); .catch(test.done);
}, },
'Test Subscribe to Stream From 0': function(test) { 'Test Subscribe to Stream From 0': function(test) {
test.expect(22); test.expect(29);
var self = this; var self = this;
var liveProcessing = false; var liveProcessing = false;
var catchUpEvents = []; var catchUpEvents = [];
@ -75,16 +96,19 @@ module.exports = {
} }
function eventAppeared(s, e) { function eventAppeared(s, e) {
if (liveProcessing) { var isLive = liveProcessing;
delayOnlyFirst(isLive ? liveEvents.length : catchUpEvents.length, function() {
if (isLive) {
liveEvents.push(e); liveEvents.push(e);
s.stop();
} else { } else {
catchUpEvents.push(e); catchUpEvents.push(e);
} }
if (isLive && liveEvents.length === 2) s.stop();
});
} }
function liveProcessingStarted() { function liveProcessingStarted() {
liveProcessing = true; liveProcessing = true;
var events = [createRandomEvent()]; var events = [createRandomEvent(), createRandomEvent()];
self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, events) self.conn.appendToStream(self.testStreamName, client.expectedVersion.any, events)
.then(function () { .then(function () {
done(); done();
@ -92,9 +116,11 @@ module.exports = {
.catch(done); .catch(done);
} }
function subscriptionDropped(connection, reason, error) { function subscriptionDropped(connection, reason, error) {
test.ok(liveEvents.length === 1, "Expecting 1 live event, got " + liveEvents.length); test.ok(liveEvents.length === 2, "Expecting 2 live event, got " + liveEvents.length);
test.testLiveEvent('liveEvents[0]', liveEvents[0]); test.testLiveEvent('liveEvents[0]', liveEvents[0]);
test.testLiveEvent('liveEvents[1]', liveEvents[1]);
test.ok(liveEvents[0].originalEventNumber, 2); test.ok(liveEvents[0].originalEventNumber, 2);
test.ok(liveEvents[1].originalEventNumber, 3);
test.ok(catchUpEvents.length === 1, "Expecting 1 catchUp event, got " + catchUpEvents.length); test.ok(catchUpEvents.length === 1, "Expecting 1 catchUp event, got " + catchUpEvents.length);
test.testReadEvent('catchUpEvents[0]', catchUpEvents[0]); test.testReadEvent('catchUpEvents[0]', catchUpEvents[0]);
test.ok(liveEvents[0].originalEventNumber, 1); test.ok(liveEvents[0].originalEventNumber, 1);

View File

@ -1,6 +1,20 @@
const uuid = require('uuid'); const uuid = require('uuid');
const client = require('../src/client'); const client = require('../src/client');
function delay(ms) {
return new Promise(function (resolve, reject) {
setTimeout(resolve, ms);
})
}
function delayOnlyFirst(count, action) {
if (count === 0) return action();
return delay(200)
.then(function () {
action();
})
}
module.exports = { module.exports = {
'Test Subscribe To Stream Happy Path': function(test) { 'Test Subscribe To Stream Happy Path': function(test) {
const resolveLinkTos = false; const resolveLinkTos = false;
@ -28,8 +42,10 @@ module.exports = {
var receivedEvents = []; var receivedEvents = [];
function eventAppeared(subscription, event) { function eventAppeared(subscription, event) {
delayOnlyFirst(receivedEvents.length, function () {
receivedEvents.push(event); receivedEvents.push(event);
if (receivedEvents.length === numberOfPublishedEvents) subscription.close(); if (receivedEvents.length === numberOfPublishedEvents) subscription.close();
});
} }
function subscriptionDropped(subscription, reason, error) { function subscriptionDropped(subscription, reason, error) {
if (error) return done(error); if (error) return done(error);