sqs-readable-stream
Version:
Create a Readable Node.JS stream from an Amazon Simple Queue Service (SQS) Queue
309 lines (250 loc) • 9.19 kB
JavaScript
var SQSReadableStream = require('./sqs-readable-stream');
var expect = require('expect.js');
var _ = require('underscore');
var messagesFixture = function () {
return _.times(10, function (i) {
return {
Body: "Message #" + i,
ReceiptHandle: "RECEIPT_" + i
};
});
};
var oldNextTick = process.nextTick;
describe('SQSReadableStream', function () {
var sqsClient,sqsStream, receiveMessageArgs, deleteMessageArgs, changeMessageVisibilityArgs, nextTickScheduled;
function runNextTicks () {
var execute = function (fn) {
fn();
};
while (nextTickScheduled.length) {
var fns = nextTickScheduled;
nextTickScheduled = [];
fns.forEach(execute);
}
}
beforeEach(function () {
nextTickScheduled = [];
process.nextTick = function (fn) {
nextTickScheduled.push(fn);
};
sqsClient = {
receiveMessage: function (params, callback) {
receiveMessageArgs = {params: params, callback: callback};
},
deleteMessage: function (params, callback) {
deleteMessageArgs = {params: params, callback: callback};
},
changeMessageVisibility: function(params, callback) {
changeMessageVisibilityArgs = {params: params, callback: callback};
}
};
receiveMessageArgs = null;
deleteMessageArgs = null;
changeMessageVisibilityArgs = null;
sqsStream = new SQSReadableStream({
queueUrl: "http://aws.example.com/queue",
sqsClient: sqsClient,
receiveMessageOptions: {
testOption: 99
},
initialBackoff: 1
});
});
afterEach(function () {
sqsStream.pause();
});
describe('Readable', function () {
it('should not contact SQS when in its initial paused state', function () {
expect(receiveMessageArgs).not.to.be.ok();
});
it('should request items from SQS when resumed', function () {
sqsStream.resume();
runNextTicks();
expect(receiveMessageArgs).to.be.ok();
});
it('should pass on QueueURL and receiveMessageOptions to receiveMessage call', function () {
sqsStream.resume();
runNextTicks();
expect(receiveMessageArgs.params.QueueUrl).to.be("http://aws.example.com/queue");
expect(receiveMessageArgs.params.testOption).to.be(99);
});
it('should pass returned queue items to the data event', function () {
var messages = [];
sqsStream.on('data', function (message) {
messages.push(message);
});
runNextTicks();
expect(receiveMessageArgs).to.be.ok();
receiveMessageArgs.callback(null, {
Messages: messagesFixture()
});
expect(_.pluck(messages, 'Body')).to.eql(_.pluck(messagesFixture(), 'Body'));
});
it('should request more items once the first lot have been processed', function () {
sqsStream.resume();
runNextTicks();
expect(receiveMessageArgs).to.be.ok();
var callback = receiveMessageArgs.callback;
receiveMessageArgs = null;
callback(null, {
Messages: messagesFixture()
});
runNextTicks();
// Should have been called again
expect(receiveMessageArgs).to.be.ok();
});
it('should not request any more items if paused', function () {
sqsStream.resume();
runNextTicks();
expect(receiveMessageArgs).to.be.ok();
var callback = receiveMessageArgs.callback;
receiveMessageArgs = null;
sqsStream.pause();
runNextTicks();
callback(null, {
Messages: messagesFixture()
});
runNextTicks();
// Should NOT have been called again
expect(receiveMessageArgs).not.to.be.ok();
});
it('should retry on errors when retryOnErrors option is given', function (done) {
sqsStream.resume();
runNextTicks();
expect(receiveMessageArgs).to.be.ok();
var callback = receiveMessageArgs.callback;
receiveMessageArgs = null;
callback("FAIL");
runNextTicks();
// Should NOT have been called again right away
expect(receiveMessageArgs).not.to.be.ok();
runNextTicks();
setTimeout(function () {
if (receiveMessageArgs) {
done();
} else {
done(new Error("Didn't retry"));
}
}, 10);
});
it('should pass on errors when retryOnErrors option is false', function () {
var error;
sqsStream = new SQSReadableStream({
queueUrl: "http://aws.example.com/queue",
sqsClient: sqsClient,
retryOnErrors: false
});
sqsStream.on('error', function (_error ) {
error = _error;
});
sqsStream.resume();
runNextTicks();
var callback = receiveMessageArgs.callback;
receiveMessageArgs = null;
callback("FAIL");
runNextTicks();
// Should NOT have been called again right away
expect(receiveMessageArgs).not.to.be.ok();
expect(error).to.be("FAIL");
runNextTicks();
setTimeout(function () {
if (!receiveMessageArgs) {
done();
} else {
done(new Error("Retried when it shouldn't have"));
}
}, 100);
});
it('should stop when queue is empty if stopOnQueueEmpty option is true', function (done) {
var ended = false;
sqsStream = new SQSReadableStream({
queueUrl: "http://aws.example.com/queue",
sqsClient: sqsClient,
stopOnQueueEmpty: true
});
sqsStream.on('end', function () {
ended = true;
});
sqsStream.resume();
runNextTicks();
expect(receiveMessageArgs).to.be.ok();
var callback = receiveMessageArgs.callback;
receiveMessageArgs = null;
callback(null, {Messages: []});
runNextTicks();
// Should NOT have been called again
expect(receiveMessageArgs).not.to.be.ok();
runNextTicks();
setTimeout(function () {
if (ended) {
done();
} else {
done(new Error("Didn't get an 'end' event"));
}
}, 20);
});
});
describe('message.deleteMessage', function () {
var messages;
beforeEach(function () {
messages = [];
sqsStream.on('data', function (message) {
messages.push(message);
});
runNextTicks();
receiveMessageArgs.callback(null, {
Messages: messagesFixture()
});
runNextTicks();
});
it('should call deleteMessage on the SQS client', function () {
var myCallback = function () {};
messages[0].deleteMessage(myCallback);
expect(deleteMessageArgs.params.QueueUrl).to.be("http://aws.example.com/queue");
expect(deleteMessageArgs.params.ReceiptHandle).to.be(messages[0].ReceiptHandle);
expect(deleteMessageArgs.callback).to.be(myCallback);
});
it('should allow the callback to be omitted', function () {
messages[0].deleteMessage();
expect(deleteMessageArgs.params.QueueUrl).to.be("http://aws.example.com/queue");
expect(deleteMessageArgs.params.ReceiptHandle).to.be(messages[0].ReceiptHandle);
expect(_.isFunction(deleteMessageArgs.callback)).to.be.ok();
});
});
describe('message.changeMessageVisibility', function () {
var messages;
beforeEach(function () {
messages = [];
sqsStream.on('data', function (message) {
messages.push(message);
});
runNextTicks();
receiveMessageArgs.callback(null, {
Messages: messagesFixture()
});
runNextTicks();
});
it('should call changeMessageVisibility on the SQS client', function () {
var myCallback = function () {};
messages[0].changeMessageVisibility(10, myCallback);
expect(changeMessageVisibilityArgs.params.QueueUrl).to.be("http://aws.example.com/queue");
expect(changeMessageVisibilityArgs.params.ReceiptHandle).to.be(messages[0].ReceiptHandle);
expect(changeMessageVisibilityArgs.params.VisibilityTimeout).to.be(10);
expect(changeMessageVisibilityArgs.callback).to.be(myCallback);
});
it('should allow the callback to be ommited', function () {
messages[0].changeMessageVisibility(10);
expect(changeMessageVisibilityArgs.params.QueueUrl).to.be("http://aws.example.com/queue");
expect(changeMessageVisibilityArgs.params.ReceiptHandle).to.be(messages[0].ReceiptHandle);
expect(changeMessageVisibilityArgs.params.VisibilityTimeout).to.be(10);
expect(_.isFunction(changeMessageVisibilityArgs.callback)).to.be.ok();
});
it('should allow the visibility and callback to be omitted', function () {
messages[0].changeMessageVisibility();
expect(changeMessageVisibilityArgs.params.QueueUrl).to.be("http://aws.example.com/queue");
expect(changeMessageVisibilityArgs.params.ReceiptHandle).to.be(messages[0].ReceiptHandle);
expect(changeMessageVisibilityArgs.params.VisibilityTimeout).to.be(0);
expect(_.isFunction(changeMessageVisibilityArgs.callback)).to.be.ok();
});
});
});