Included more tests for the doc source, expanded the capabilities and configurability of the StubbedClient.

This commit is contained in:
Spencer Alger 2014-03-12 10:18:51 -07:00
parent 41083d6ff2
commit b4917b7dbd
4 changed files with 292 additions and 36 deletions

View file

@ -1,9 +1,177 @@
define(function (require) {
var createCourier = require('test_utils/create_courier');
var stubbedClient = require('test_utils/stubbed_client');
var sinon = require('test_utils/auto_release_sinon');
var _ = require('lodash');
return function extendCourierSuite() {
describe('DocSource class', function () {
it('tracks the version of the document');
it('can be used without saving the doc');
it('provides a way to keep two objects synced between tabs');
it('tracks the version of the document', function (done) {
var version = 51;
var courier = createCourier(stubbedClient(function (method, params, cb) {
cb(void 0, stubbedClient.doc({ _version: version }));
}));
var source = courier
.createSource('doc').index('fake').type('fake').id('fake')
.on('results', function (doc) {
expect(source._getVersion()).to.eql(version);
expect(courier._getRefFor(source).version).to.eql(version);
done();
});
courier.start();
});
it('updates to a doc will propogate to other docs with the same index/type/id', function (done) {
var client = (function () {
// fake server state
var version = 0;
var doc = { hi: 'fallacy' };
return stubbedClient({
update: function (params, cb) {
_.assign(doc, params.body.doc);
version++;
cb(void 0, { ok: true });
},
default: function (method, params, cb) {
cb(void 0, stubbedClient.doc({ _source: doc, _version: version }));
}
});
}());
var courier = createCourier(client);
var update = { hi: 'truth' };
// updating this
var pitcher = courier.createSource('doc').index('fake').type('fake').id('fake')
.doUpdate(update);
// should update this
var catcher = courier.createSource('doc').index('fake').type('fake').id('fake')
.on('results', function (doc) {
expect(doc._source).to.eql(update);
done();
});
});
it('clears the stored version when a document has been deleted', function (done) {
var client = (function () {
// fake server state
var doc = { hi: 'fallacy' };
return stubbedClient({
delete: function (params, cb) {
doc = null;
cb(void 0, { ok: true });
},
default: function (method, params, cb) {
if (doc) {
cb(void 0, stubbedClient.doc({ _source: doc }));
} else {
cb(void 0, stubbedClient.doc({ found: false, _source: null, _version: null }));
}
}
});
}());
var courier = createCourier(client);
var source = courier.createSource('doc')
.index('fake')
.type('fake')
.id('fake')
.on('results', function (doc) {
if (doc.found) {
client.delete({}, function () {
source.fetch();
});
} else {
expect(courier._getRefFor(source).version).to.be(void 0);
expect(source._getVersion()).to.be(void 0);
done();
}
});
courier.start();
});
it('checks localStorage for changes to the stored version, which will trigger the doc to be refetched', function (done) {
var version = 11234;
var courier = createCourier(stubbedClient(function (method, params, cb) {
cb(void 0, stubbedClient.doc({ _version: version }));
}));
var count = 0;
courier.docInterval(10);
var source = courier.createSource('doc')
.index('fake')
.type('fake')
.id('fake')
.on('results', function (doc) {
switch (count++) {
case 0:
// simulate removing the version in another tab
localStorage.removeItem(source._versionKey());
// get version should now be returning undefined
expect(source._getVersion()).to.eql(void 0);
// tell the courier to check docs 1 ms now
courier.docInterval(1);
break;
case 1:
// doc version should now be populated
expect(source._getVersion()).to.eql(version);
done();
}
});
courier.start();
});
describe('#doIndex', function () {
it('reindexes the doc using the hash passed in', function (done) {
var client = (function () {
var version = 1;
var doc = { initial: true };
return stubbedClient({
index: function (params, cb) {
doc = _.clone(params.body);
version ++;
cb(void 0, { ok: true });
},
mget: function (params, cb) {
cb(void 0, stubbedClient.doc({ _version: version, _source: doc }));
}
});
}());
var courier = createCourier(client);
var count = 0;
courier.docInterval(10);
var source = courier.createSource('doc')
.index('fake')
.type('fake')
.id('fake')
.on('results', function (doc) {
switch (count ++) {
case 0:
expect(doc._source).to.have.property('initial', true);
source.doIndex({ second: true });
break;
case 1:
expect(doc._source).to.not.have.property('initial');
expect(doc._source).to.have.property('second', true);
done();
break;
}
});
courier.start();
});
});
});
};
});

View file

@ -6,13 +6,12 @@ define(function (require) {
describe('events', function () {
describe('error', function () {
it('emits when the client fails', function (done) {
var err = new Error('Error!');
var courier = createCourier({
client: stubbedClient(function (method, params, cb) { cb(err); })
client: stubbedClient(function (method, params, cb) { cb(new Error()); })
});
courier.on('error', function (emittedError) {
expect(emittedError).to.be(err);
expect(emittedError).to.be.an(Error);
done();
});

View file

@ -1,10 +1,26 @@
define(function (require) {
var Courier = require('courier/courier');
var EsTransport = require('bower_components/elasticsearch/elasticsearch').Transport;
var StubbedClient = require('test_utils/stubbed_client');
var _ = require('lodash');
var activeCouriers = [];
function createCourier(opts) {
// allow passing in a client directly
if (
opts
&& (
opts instanceof StubbedClient
|| opts.transport instanceof EsTransport
)
)
{
opts = {
client: opts
};
}
var courier = new Courier(opts);
// after each test this list is cleared

View file

@ -1,9 +1,97 @@
define(function (require) {
var _ = require('lodash');
var nativeSetTimeout = window.setTimeout;
var nativeClearTimeout = window.clearTimeout;
var methodsToStub = [
'msearch',
'mget',
'index',
'update',
'delete'
];
var defaultResponses = {
msearch: function (params, cb) {
cb(null, responses(countMultiRequests(params)));
},
mget: function (params, cb) {
cb(null, responses(countMultiRequests(params)));
}
};
/**
* Create a "client" that will mock several functions
* but really just defers to the `respond` method. In many cases
* (so far) very simple logic is required to push empty/irrelevent
* responses back (which is more than fine for the courier)
*
* @param {[type]} respond - a function that will be called after a short
* timeout to respond in place of a stubbed method.
* @return {[type]} [description]
*/
function StubbedClient(responder) {
if (!(this instanceof StubbedClient)) return new StubbedClient(responder);
var stub = this;
stub.__responder = responder || defaultResponses;
if (typeof this.__responder === 'object') {
// transform the responder object into a function that can be called like the others
stub.__responder = (function (options) {
return function (method, params, cb) {
if (options[method]) return options[method](params, cb);
if (options.default) return options.default(method, params, cb);
throw new Error('responder for "default" or "' + method + '" required');
};
})(stub.__responder);
}
stub.callCount = 0;
stub.abortCalled = 0;
methodsToStub.forEach(function (method) {
// count every call to this method
stub[method].callCount = 0;
});
return stub;
}
methodsToStub.forEach(function (method) {
// create a stub for each method
StubbedClient.prototype[method] = function (params, cb) {
var stub = this;
// increment global counters
stub.callCount ++;
// inc this method's counter
stub[method].callCount++;
if (typeof params === 'function') {
// allow calling with just a callback
cb = params;
params = {};
}
// call the responder after 3 ms to simulate a very quick response
var id = nativeSetTimeout(_.partial(stub.__responder, method, params, cb), 3);
// return an aborter, that has a short but reasonable amount of time to be called
return {
abort: function () {
nativeClearTimeout(id);
stub.abortCalled ++;
}
};
};
});
// cound the number of requests in a multi request bulk body
function countMultiRequests(params) {
return (params.body) ? Math.floor(params.body.split('\n').length / 2) : 0;
}
// create a generic response for N requests
function responses(n) {
@ -27,39 +115,24 @@ define(function (require) {
return { responses: resp };
}
function stubbedClient(respond) {
respond = respond || function (method, params, cb) {
var n = (params.body) ? Math.floor(params.body.split('\n').length / 2) : 0;
cb(null, responses(n));
};
var stub = {
callCount: 0,
abortCalled: 0
};
_.each(['msearch', 'mget'], function (method) {
stub[method] = function (params, cb) {
stub[method].callCount++;
stub.callCount ++;
var id = nativeSetTimeout(_.partial(respond, method, params, cb), 3);
return {
abort: function () {
nativeClearTimeout(id);
stub.abortCalled ++;
}
};
};
stub[method].callCount = 0;
// create a generic response with a single doc, that uses
// the passed in response but fills in some defaults
function doc(d) {
d = _.defaults(d || {}, {
found: true,
_version: 1,
_source: {}
});
return stub;
return {
docs: [ d ]
};
}
stubbedClient.errorReponses = errorReponses;
stubbedClient.responses = responses;
StubbedClient.errorReponses = errorReponses;
StubbedClient.responses = responses;
StubbedClient.doc = doc;
return stubbedClient;
return StubbedClient;
});