features/extra/helpers.js (125 lines of code) (raw):
module.exports = {
assert: require('./assertions').assert,
uniqueName: function uniqueName(base, sep) {
if (sep === undefined) sep = '-';
if (base === '') return '';
return base + sep + new Date().getTime();
},
/**
* Call this function with a block that will be executed multiple times
* to deal with eventually consistent conditions.
*
* this.When(/^I something is eventually consistent$/, function(callback) {
* this.eventually(callback, function(next) {
* doSomething(function(response) {
* if (response != notWhatIExpect) {
* next.fail();
* } else {
* next();
* }
* });
* });
* });
*
* You can pass in a few options after the function:
*
* delay: The number of milliseconds to delay before retrying.
* backoff: Add this number of milliseconds to the delay between each attempt.
* maxTime: Maximum duration of milliseconds to wait for success.
*/
eventually: function eventually(callback, block, options) {
if (!options) options = {};
if (!options.delay) options.delay = 0;
if (!options.backoff) options.backoff = 500;
if (!options.maxTime) options.maxTime = 5;
var delay = options.delay;
var started = this.AWS.util.date.getDate();
var self = this;
var retry = function() { callback(); };
retry.fail = function(err) {
var now = self.AWS.util.date.getDate();
if (now - started < options.maxTime * 1000) {
setTimeout(function () {
delay += options.backoff;
block.call(self, retry);
}, delay);
} else {
callback.fail(err || new Error('Eventually block timed out'));
}
};
block.call(this, retry);
},
/**
* A short-cut for calling a service operation and waiting for it to
* finish execution before moving onto the next step in the scenario.
*/
request: function request(svc, operation, params, next, extra) {
var world = this;
if (!svc) svc = this.service;
if (typeof svc === 'string') svc = this[svc];
svc[operation](params, function(err, data) {
world.response = this;
world.error = err;
world.data = data;
try {
if (typeof next.condition === 'function') {
var condition = next.condition.call(world, world);
if (!condition) {
next.fail(new Error('Request success condition failed'));
return;
}
}
if (extra) {
extra.call(world, world.response);
next.call(world);
}
else if (extra !== false && err) {
world.unexpectedError(world.response, next);
} else {
next.call(world);
}
} catch (err) {
next.fail(err);
}
});
},
/**
* Given a response that contains an error, this fails the current
* step with a formatted error message that indicates which service and
* operation failed.
*/
unexpectedError: function unexpectedError(resp, next) {
var svc = resp.request.service.api.serviceName;
var op = resp.request.operation;
var code = resp.error.code;
var msg = resp.error.message;
var err = 'Received unexpected error from ' + svc + '.' + op + ', ' + code + ': ' + msg;
next.fail(new Error(err));
},
/**
* Cache bucket names used for cleanup after all features have run.
*/
cacheBucketName: function(bucket) {
var fs = require('fs');
var path = require('path');
var filePath = path.resolve('integ.buckets.json');
var cache;
if (fs.existsSync(filePath)) {
try {
cache = JSON.parse(fs.readFileSync(filePath));
cache.buckets.push(bucket);
fs.writeFileSync(filePath, JSON.stringify(cache));
} catch (fileErr) {
throw fileErr;
}
} else {
cache = {};
cache.buckets = [bucket];
fs.writeFileSync(filePath, JSON.stringify(cache));
}
},
/**
* Creates a fixture file of given size and returns the path.
*/
createFile: function(size, name) {
var fs = require('fs');
var path = require('path');
name = this.uniqueName(name);
// Cannot set this as a world property because the world
// is cleaned up before the AfterFeatures hook is fired.
var fixturePath = path.resolve('./features/extra/fixtures/tmp');
if (!fs.existsSync(fixturePath)) fs.mkdirSync(fixturePath);
var filename = path.join(fixturePath, name);
var body;
if (typeof size === 'string') {
switch (size) {
case 'empty': body = Buffer.alloc(0); break;
case 'small': body = Buffer.alloc(1024 * 1024); break;
case 'large': body = Buffer.alloc(1024 * 1024 * 20); break;
}
} else if (typeof size === 'number') {
body = Buffer.alloc(size);
}
fs.writeFileSync(filename, body);
return filename;
},
/**
* Creates and returns a buffer of given size
*/
createBuffer: function(size) {
var match;
var buffer;
if (match = size.match(/(\d+)KB/)) {
buffer = Buffer.alloc(parseInt(match[1]) * 1024);
} else if (match = size.match(/(\d+)MB/)) {
buffer = Buffer.alloc(parseInt(match[1]) * 1024 * 1024);
} else {
switch (size) {
case 'empty': buffer = Buffer.alloc(0); break;
case 'small': buffer = Buffer.alloc(1024 * 1024); break;
case 'large': buffer = Buffer.alloc(1024 * 1024 * 20); break;
default: return Buffer.alloc(1024 * 1024);
}
}
buffer.fill('x');
return buffer;
}
};