Merge pull request #1144 from strongloop/feature/parameterize-kvao-tests-2x

test/kvao: add connectorCapabilities options [2.x]
This commit is contained in:
Miroslav Bajtoš 2016-10-19 13:40:28 +02:00 committed by GitHub
commit 959a821be5
7 changed files with 86 additions and 29 deletions

19
test/helpers/bdd-if.js Normal file
View File

@ -0,0 +1,19 @@
// Copyright IBM Corp. 2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
exports.describeIf = function describeIf(cond, name, fn) {
if (cond)
describe(name, fn);
else
describe.skip(name, fn);
};
exports.itIf = function itIf(cond, name, fn) {
if (cond)
it(name, fn);
else
it.skip(name, fn);
};

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
var debug = require('debug')('test'); var debug = require('debug')('test');
var extend = require('util')._extend;
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
@ -8,6 +9,12 @@ if (!global.Promise)
global.Promise = require('bluebird'); global.Promise = require('bluebird');
module.exports = function(dataSourceFactory, connectorCapabilities) { module.exports = function(dataSourceFactory, connectorCapabilities) {
connectorCapabilities = extend({
// Even when the backend supports millisecond precision,
// it's better to use intervals at least 10ms long in the tests
ttlPrecision: 10,
}, connectorCapabilities);
describe('KeyValue API', function loadAllTestFiles() { describe('KeyValue API', function loadAllTestFiles() {
var testRoot = path.resolve(__dirname, 'kvao'); var testRoot = path.resolve(__dirname, 'kvao');
var testFiles = fs.readdirSync(testRoot); var testFiles = fs.readdirSync(testRoot);

View File

@ -1,11 +1,18 @@
'use strict'; 'use strict';
var bdd = require('../helpers/bdd-if');
var should = require('should'); var should = require('should');
var helpers = require('./_helpers'); var helpers = require('./_helpers');
var Promise = require('bluebird'); var Promise = require('bluebird');
module.exports = function(dataSourceFactory, connectorCapabilities) { module.exports = function(dataSourceFactory, connectorCapabilities) {
describe('expire', function() { // While we support millisecond precision, for the purpose of tests
// it's better to use intervals at least 10ms long.
var ttlPrecision = connectorCapabilities.ttlPrecision || 10;
var canExpire = connectorCapabilities.canExpire !== false;
bdd.describeIf(canExpire, 'expire', function() {
var CacheItem; var CacheItem;
beforeEach(function unpackContext() { beforeEach(function unpackContext() {
CacheItem = helpers.givenCacheItem(dataSourceFactory); CacheItem = helpers.givenCacheItem(dataSourceFactory);
@ -14,7 +21,7 @@ module.exports = function(dataSourceFactory, connectorCapabilities) {
it('sets key ttl - Callback API', function(done) { it('sets key ttl - Callback API', function(done) {
CacheItem.set('a-key', 'a-value', function(err) { CacheItem.set('a-key', 'a-value', function(err) {
if (err) return done(err); if (err) return done(err);
CacheItem.expire('a-key', 1, function(err) { CacheItem.expire('a-key', ttlPrecision, function(err) {
if (err) return done(err); if (err) return done(err);
setTimeout(function() { setTimeout(function() {
CacheItem.get('a-key', function(err, value) { CacheItem.get('a-key', function(err, value) {
@ -22,22 +29,22 @@ module.exports = function(dataSourceFactory, connectorCapabilities) {
should.equal(value, null); should.equal(value, null);
done(); done();
}); });
}, 20); }, 2 * ttlPrecision);
}); });
}); });
}); });
it('sets key ttl - Promise API', function() { it('sets key ttl - Promise API', function() {
return Promise.resolve(CacheItem.set('a-key', 'a-value')) return Promise.resolve(CacheItem.set('a-key', 'a-value'))
.then(function() { return CacheItem.expire('a-key', 1); }) .then(function() { return CacheItem.expire('a-key', ttlPrecision); })
.delay(20) .delay(2 * ttlPrecision)
.then(function() { return CacheItem.get('a-key'); }) .then(function() { return CacheItem.get('a-key'); })
.then(function(value) { should.equal(value, null); }); .then(function(value) { should.equal(value, null); });
}); });
it('returns error when expiring a key that has expired', function() { it('returns error when expiring a key that has expired', function() {
return Promise.resolve(CacheItem.set('expired-key', 'a-value', 1)) return Promise.resolve(CacheItem.set('expired-key', 'a-value', ttlPrecision))
.delay(20) .delay(2 * ttlPrecision)
.then(function() { return CacheItem.expire('expired-key', 1000); }) .then(function() { return CacheItem.expire('expired-key', 1000); })
.then( .then(
function() { throw new Error('expire() should have failed'); }, function() { throw new Error('expire() should have failed'); },
@ -48,7 +55,7 @@ module.exports = function(dataSourceFactory, connectorCapabilities) {
}); });
it('returns error when key does not exist', function() { it('returns error when key does not exist', function() {
return CacheItem.expire('key-does-not-exist', 1).then( return CacheItem.expire('key-does-not-exist', ttlPrecision).then(
function() { throw new Error('expire() should have failed'); }, function() { throw new Error('expire() should have failed'); },
function(err) { function(err) {
err.message.should.match(/key-does-not-exist/); err.message.should.match(/key-does-not-exist/);

View File

@ -5,6 +5,8 @@ var helpers = require('./_helpers');
var Promise = require('bluebird'); var Promise = require('bluebird');
module.exports = function(dataSourceFactory, connectorCapabilities) { module.exports = function(dataSourceFactory, connectorCapabilities) {
var TTL_PRECISION = connectorCapabilities.ttlPrecision;
describe('get/set', function() { describe('get/set', function() {
var CacheItem; var CacheItem;
beforeEach(function unpackContext() { beforeEach(function unpackContext() {
@ -68,8 +70,8 @@ module.exports = function(dataSourceFactory, connectorCapabilities) {
}); });
it('honours options.ttl', function() { it('honours options.ttl', function() {
return Promise.resolve(CacheItem.set('a-key', 'a-value', { ttl: 10 })) return Promise.resolve(CacheItem.set('a-key', 'a-value', { ttl: TTL_PRECISION }))
.delay(20) .delay(2 * TTL_PRECISION)
.then(function() { return CacheItem.get('a-key'); }) .then(function() { return CacheItem.get('a-key'); })
.then(function(value) { should.equal(value, null); }); .then(function(value) { should.equal(value, null); });
}); });
@ -79,22 +81,22 @@ module.exports = function(dataSourceFactory, connectorCapabilities) {
return CacheItem.get('key-does-not-exist') return CacheItem.get('key-does-not-exist')
.then(function(value) { should.equal(value, null); }); .then(function(value) { should.equal(value, null); });
}); });
it('converts numeric options arg to options.ttl', function() {
return Promise.resolve(CacheItem.set('a-key', 'a-value', 10))
.delay(20)
.then(function() { return CacheItem.get('a-key'); })
.then(function(value) { should.equal(value, null); });
});
}); });
describe('set', function() { describe('set', function() {
it('converts numeric options arg to options.ttl', function() {
return Promise.resolve(CacheItem.set('a-key', 'a-value', TTL_PRECISION))
.delay(2 * TTL_PRECISION)
.then(function() { return CacheItem.get('a-key'); })
.then(function(value) { should.equal(value, null); });
});
it('resets TTL timer', function() { it('resets TTL timer', function() {
return Promise.resolve(CacheItem.set('a-key', 'a-value', { ttl: 10 })) return Promise.resolve(CacheItem.set('a-key', 'a-value', { ttl: TTL_PRECISION }))
.then(function() { .then(function() {
return CacheItem.set('a-key', 'another-value'); // no TTL return CacheItem.set('a-key', 'another-value'); // no TTL
}) })
.delay(20) .delay(2 * TTL_PRECISION)
.then(function() { return CacheItem.get('a-key'); }) .then(function() { return CacheItem.get('a-key'); })
.then(function(value) { should.equal(value, 'another-value'); }); .then(function(value) { should.equal(value, 'another-value'); });
}); });

View File

@ -1,13 +1,16 @@
'use strict'; 'use strict';
var asyncIterators = require('async-iterators'); var asyncIterators = require('async-iterators');
var bdd = require('../helpers/bdd-if');
var helpers = require('./_helpers'); var helpers = require('./_helpers');
var Promise = require('bluebird'); var Promise = require('bluebird');
var should = require('should'); var should = require('should');
var toArray = Promise.promisify(asyncIterators.toArray); var toArray = Promise.promisify(asyncIterators.toArray);
module.exports = function(dataSourceFactory, connectorCapabilities) { module.exports = function(dataSourceFactory, connectorCapabilities) {
describe('iterateKeys', function() { var canIterateKeys = connectorCapabilities.canIterateKeys !== false;
bdd.describeIf(canIterateKeys, 'iterateKeys', function() {
var CacheItem; var CacheItem;
beforeEach(function unpackContext() { beforeEach(function unpackContext() {
CacheItem = helpers.givenCacheItem(dataSourceFactory); CacheItem = helpers.givenCacheItem(dataSourceFactory);

View File

@ -1,11 +1,14 @@
'use strict'; 'use strict';
var bdd = require('../helpers/bdd-if');
var helpers = require('./_helpers'); var helpers = require('./_helpers');
var Promise = require('bluebird'); var Promise = require('bluebird');
var should = require('should'); var should = require('should');
module.exports = function(dataSourceFactory, connectorCapabilities) { module.exports = function(dataSourceFactory, connectorCapabilities) {
describe('keys', function() { var canIterateKeys = connectorCapabilities.canIterateKeys !== false;
bdd.describeIf(canIterateKeys, 'keys', function() {
var CacheItem; var CacheItem;
beforeEach(function unpackContext() { beforeEach(function unpackContext() {
CacheItem = helpers.givenCacheItem(dataSourceFactory); CacheItem = helpers.givenCacheItem(dataSourceFactory);
@ -54,7 +57,8 @@ module.exports = function(dataSourceFactory, connectorCapabilities) {
}); });
}); });
it('handles large key set', function() { var largeKeySets = connectorCapabilities.canIterateLargeKeySets !== false;
bdd.itIf(largeKeySets, 'handles large key set', function() {
var expectedKeys = []; var expectedKeys = [];
for (var ix = 0; ix < 1000; ix++) for (var ix = 0; ix < 1000; ix++)
expectedKeys.push('key-' + ix); expectedKeys.push('key-' + ix);

View File

@ -1,11 +1,25 @@
'use strict'; 'use strict';
var bdd = require('../helpers/bdd-if');
var should = require('should'); var should = require('should');
var helpers = require('./_helpers'); var helpers = require('./_helpers');
var Promise = require('bluebird'); var Promise = require('bluebird');
module.exports = function(dataSourceFactory, connectorCapabilities) { module.exports = function(dataSourceFactory, connectorCapabilities) {
describe('ttl', function() { var TTL_PRECISION = connectorCapabilities.ttlPrecision;
// Use ~1s for stores with precision of 1 ms,
// about 3s for stores with precision of 1s.
var INITIAL_TTL = Math.max(TTL_PRECISION + 1000, TTL_PRECISION * 3);
// A small delay to allow the backend to process the request, run any
// TTL/expire checks, etc. Use 1ms for backends supporting sub-10ms
// resolution to ensure the delay is not too short..
var SMALL_DELAY = Math.max(1, Math.floor(TTL_PRECISION / 10));
var canQueryTtl = connectorCapabilities.canQueryTtl !== false;
bdd.describeIf(canQueryTtl, 'ttl', function() {
var CacheItem; var CacheItem;
beforeEach(function unpackContext() { beforeEach(function unpackContext() {
CacheItem = helpers.givenCacheItem(dataSourceFactory); CacheItem = helpers.givenCacheItem(dataSourceFactory);
@ -14,19 +28,19 @@ module.exports = function(dataSourceFactory, connectorCapabilities) {
it('gets TTL when key with unexpired TTL exists - Promise API', it('gets TTL when key with unexpired TTL exists - Promise API',
function() { function() {
return Promise.resolve( return Promise.resolve(
CacheItem.set('a-key', 'a-value', { ttl: 1000 })) CacheItem.set('a-key', 'a-value', { ttl: INITIAL_TTL }))
.delay(1) .delay(SMALL_DELAY)
.then(function() { return CacheItem.ttl('a-key'); }) .then(function() { return CacheItem.ttl('a-key'); })
.then(function(ttl) { ttl.should.be.within(1, 1000); }); .then(function(ttl) { ttl.should.be.within(1, INITIAL_TTL); });
}); });
it('gets TTL when key with unexpired TTL exists - Callback API', it('gets TTL when key with unexpired TTL exists - Callback API',
function(done) { function(done) {
CacheItem.set('a-key', 'a-value', { ttl: 1000 }, function(err) { CacheItem.set('a-key', 'a-value', { ttl: INITIAL_TTL }, function(err) {
if (err) return done(err); if (err) return done(err);
CacheItem.ttl('a-key', function(err, ttl) { CacheItem.ttl('a-key', function(err, ttl) {
if (err) return done(err); if (err) return done(err);
ttl.should.be.within(1, 1000); ttl.should.be.within(1, INITIAL_TTL);
done(); done();
}); });
}); });
@ -40,7 +54,8 @@ module.exports = function(dataSourceFactory, connectorCapabilities) {
it('fails when getting TTL for a key with expired TTL', function() { it('fails when getting TTL for a key with expired TTL', function() {
return Promise.resolve( return Promise.resolve(
CacheItem.set('expired-key', 'a-value', { ttl: 10 })).delay(20) CacheItem.set('expired-key', 'a-value', { ttl: TTL_PRECISION }))
.delay(2 * TTL_PRECISION)
.then(function() { .then(function() {
return CacheItem.ttl('expired-key'); return CacheItem.ttl('expired-key');
}) })