dynamodb-test
Version:
Create and destroy DynamoDB and Dynalite tables for use in tape tests
228 lines (185 loc) • 6.1 kB
JavaScript
var crypto = require('crypto');
var AWS = require('aws-sdk');
var _ = require('underscore');
var stream = require('stream');
var Dyno = require('dyno');
var dynalite = require('dynalite')({
createTableMs: 0,
updateTableMs: 0,
deleteTableMs: 0
});
var listening = false;
module.exports = ddbtest;
module.exports.fixedName = function(test, tableName, tableDef) {
var dynamo = ddbtest(test, tableName, tableDef);
dynamo.tableName = dynamo.tableDef.TableName = tableName;
return dynamo;
};
function ddbtest(test, projectName, tableDef, region) {
var live = !!region;
tableDef = _(tableDef).clone();
function getKeys(item) {
var keyNames = tableDef.KeySchema.map(function(key) {
return key.AttributeName;
});
return keyNames.reduce(function(key, name) {
key[name] = item[name];
return key;
}, {});
}
var dynamodb = {};
dynamodb.tableName = tableDef.TableName = [
'test',
projectName,
crypto.randomBytes(4).toString('hex')
].join('-');
dynamodb.tableDef = tableDef;
var options = dynamodb.config = live ? { region: region } : {
region: 'fake',
accessKeyId: 'fake',
secretAccessKey: 'fake',
endpoint: 'http://localhost:4567'
};
dynamodb.dynamo = new AWS.DynamoDB(options);
dynamodb.dyno = Dyno(_({ table: dynamodb.tableName }).extend(options));
var tableRunning = false;
function start(assert, callback) {
if (live) assert.timeoutAfter(300000);
if (tableRunning) return assert.end();
function done(err) {
if (err) throw err;
tableRunning = true;
callback();
}
if (live) return dynamodb.dyno.createTable(tableDef, done);
if (listening) return dynamodb.dyno.createTable(tableDef, done);
dynalite.listen(4567, function(err) {
if (err) throw err;
listening = true;
dynamodb.dyno.createTable(tableDef, done);
});
}
dynamodb.start = function() {
test('[dynamodb-test] create table', function(assert) {
start(assert, function() {
assert.end();
});
});
};
dynamodb.delete = function() {
test('[dynamodb-test] delete table', function(assert) {
if (live) assert.timeoutAfter(300000);
if (!tableRunning) return assert.end();
dynamodb.dyno.deleteTable({
TableName: dynamodb.tableName
}, function(err) {
if (err) throw err;
tableRunning = false;
assert.end();
});
});
};
dynamodb.load = function(fixtures) {
test('[dynamodb-test] load fixtures', function(assert) {
if (!tableRunning) start(assert, load);
else load();
function load() {
if (live) assert.timeoutAfter(300000);
var params = { RequestItems: {} };
params.RequestItems[dynamodb.tableName] = fixtures.map(function(item) {
return { PutRequest: { Item: item } };
});
var requestSet = dynamodb.dyno.batchWriteItemRequests(params);
var attempts = 0;
(function write(requestSet) {
requestSet.sendAll(10, function(err, responses, unprocessed) {
if (err) throw err;
attempts++;
if (unprocessed && unprocessed.length)
return setTimeout(write, Math.pow(2, attempts), unprocessed);
assert.end();
});
})(requestSet);
}
});
};
dynamodb.empty = function() {
test('[dynamodb-test] empty table', function(assert) {
if (!tableRunning) start(assert, empty);
else empty();
function empty() {
if (live) {
assert.timeoutAfter(300000);
var deletes = new stream.Writable({ objectMode: true });
deletes.pending = false;
var keys = [];
keys.send = function(callback) {
var params = { RequestItems: {} };
params.RequestItems[dynamodb.tableName] = keys.splice(0, 25).map(function(key) {
return { DeleteRequest: { Key: key } };
});
(function destroy(params) {
deletes.pending = true;
dynamodb.dyno.batchWriteItem(params, function(err, data) {
deletes.pending = false;
if (err) throw err;
if (data.UnprocessedItems && Object.keys(data.UnprocessedItems).length)
return destroy({RequestItems: data.UnprocessedItems });
callback();
});
})(params);
};
deletes._write = function(item, enc, callback) {
keys.push(getKeys(item));
if (keys.length < 25) return callback();
keys.send(callback);
};
var end = deletes.end.bind(deletes);
deletes.end = function() {
if (deletes.pending) return setImmediate(deletes.end);
if (!keys.length) return end();
keys.send(end);
};
dynamodb.dyno.scanStream({ ConsistentRead: true })
.pipe(deletes)
.on('finish', function() {
assert.end();
});
return;
}
dynamodb.dyno.deleteTable(dynamodb.tableName, function(err) {
if (err) throw err;
tableRunning = false;
dynamodb.dyno.createTable(dynamodb.tableDef, function(err) {
if (err) throw err;
tableRunning = true;
assert.end();
});
});
}
});
};
dynamodb.test = function(name, fixtures, callback) {
dynamodb.empty();
if (typeof fixtures === 'function') {
callback = fixtures;
fixtures = null;
}
if (fixtures && fixtures.length) dynamodb.load(fixtures);
test(name, callback);
dynamodb.empty();
};
if (!live) dynamodb.close = function() {
test('[dynamodb-test] close dynalite', function(assert) {
dynamodb.dyno.deleteTable(dynamodb.tableName, function(err) {
if (err) throw err;
dynalite.close(function(err) {
if (err) throw err;
listening = false;
assert.end();
});
});
});
};
return dynamodb;
}