From 4e124150a860e3859d8864e3e5890eda33ace13b Mon Sep 17 00:00:00 2001 From: Troy Jones Date: Wed, 13 Oct 2021 07:05:35 -0600 Subject: [PATCH 1/5] Send MessageAttributes when moving a message: resolves #21 --- src/sqs.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/sqs.js b/src/sqs.js index 6cb41b7..009ac95 100644 --- a/src/sqs.js +++ b/src/sqs.js @@ -1,14 +1,14 @@ const createClient = (sqs) => { - const sendMessage = (QueueUrl, MessageBody) => new Promise((resolve, reject) => { + const sendMessage = (QueueUrl, MessageBody, MessageAttributes) => new Promise((resolve, reject) => { sqs.sendMessage( - { QueueUrl, MessageBody }, + { QueueUrl, MessageBody, MessageAttributes }, (error, data) => (error ? reject(error) : resolve(data)), ); }); const receiveMessage = QueueUrl => new Promise((resolve, reject) => { sqs.receiveMessage( - { QueueUrl }, + { QueueUrl, MessageAttributeNames: ['All'] }, (error, data) => (error ? reject(error) : resolve(data.Messages[0])), ); }); @@ -46,8 +46,22 @@ const createClient = (sqs) => { } const { Body, ReceiptHandle } = receivedMessage; + var { MessageAttributes } = receivedMessage; - await sendMessage(targetQueueUrl, Body); + const deleteArrays = (obj) => { + if (typeof obj !== "undefined") { + Object.keys(obj).forEach(key => { + if (Array.isArray(obj[key])) { + delete obj[key]; // Remove invalid Array values in MessageAttributes + } else if (typeof obj[key] === "object") { + deleteArrays(obj[key]); + } + }); + } + }; + deleteArrays(MessageAttributes); + + await sendMessage(targetQueueUrl, Body, MessageAttributes); await deleteMessage(sourceQueueUrl, ReceiptHandle); resolve(ReceiptHandle); From 5305223495f0d81d843a0c50064d9732dfa15466 Mon Sep 17 00:00:00 2001 From: Troy Jones Date: Wed, 13 Oct 2021 06:52:38 -0600 Subject: [PATCH 2/5] Add option to specify max number of messages to move: resolves #19 --- README.md | 2 +- src/helper.js | 10 ++++++++++ src/index.js | 16 +++++++++++++++- src/main.js | 17 ++++++++++++----- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 82e1dc2..3a7d08a 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ $ aws-move-queue-messages With all optional CLI arguments: ```sh -$ aws-move-queue-messages -r [AWS-REGION] -y +$ aws-move-queue-messages -r [AWS-REGION] -m 100 -y ``` Any CLI argument or option you do not specify will fallback to a CLI prompt. The `-y` CLI option will answer the confirmation prompt automatically with "yes". diff --git a/src/helper.js b/src/helper.js index dc9ff35..eef8dd7 100644 --- a/src/helper.js +++ b/src/helper.js @@ -6,6 +6,16 @@ const validateUrl = (input) => { return valid; }; +const validateMaxMessages = (input) => { + let valid = true; + if (!input.match(/^[1-9][0-9]*$/)) { + valid = 'Please enter a valid max number of messages greater than 0!' + } + + return valid; +} + module.exports = { validateUrl, + validateMaxMessages }; diff --git a/src/index.js b/src/index.js index 9d6d313..2349903 100644 --- a/src/index.js +++ b/src/index.js @@ -3,7 +3,7 @@ const aws = require('aws-sdk'); const program = require('commander'); const { prompt } = require('inquirer'); -const { validateUrl } = require('./helper'); +const { validateMaxMessages, validateUrl } = require('./helper'); const { handle } = require('./main'); const { createClient } = require('./sqs'); @@ -28,6 +28,13 @@ const toQuestion = { validate: validateUrl, }; +const maxQuestion = { + type: 'input', + name: 'maxMessages', + message: 'Enter the max number of messages:', + validate: validateMaxMessages, +} + const handleAction = (from, to, options) => { const questions = []; @@ -35,6 +42,10 @@ const handleAction = (from, to, options) => { questions.push(regionQuestion); } + if (!options.maxMessages) { + questions.push(maxQuestion) + } + if (!from) { questions.push(fromQuestion); } @@ -45,6 +56,7 @@ const handleAction = (from, to, options) => { prompt(questions).then(async ({ awsRegion = options.region, + maxMessages = options.maxMessages, sourceQueueUrl = from, targetQueueUrl = to, }) => { @@ -55,6 +67,7 @@ const handleAction = (from, to, options) => { try { count = await handle({ + maxMessages, sourceQueueUrl, targetQueueUrl, sqs, @@ -73,6 +86,7 @@ const handleAction = (from, to, options) => { program .arguments('[from] [to]') .option('-r, --region [value]', 'The AWS region') + .option('-m, --maxMessages [value]', 'Max number of messages') .option('-y, --yes', 'Non interactive message moving') .action(handleAction) .parse(process.argv); diff --git a/src/main.js b/src/main.js index 9e84174..b01d782 100644 --- a/src/main.js +++ b/src/main.js @@ -1,6 +1,7 @@ const { Spinner } = require('clui'); const handle = async ({ + maxMessages, sourceQueueUrl, targetQueueUrl, sqs, @@ -10,16 +11,22 @@ const handle = async ({ const count = await sqs.getCount(sourceQueueUrl); await sqs.getCount(targetQueueUrl); - if (count === 0) { + if (parseInt(count) === 0) { throw new Error(`The queue ${sourceQueueUrl} is empty!`); } + maxMessages = parseInt(maxMessages); + moveCount = count + if(count > maxMessages) { + moveCount = maxMessages + } + if (!skipPrompt) { const { move } = await prompt([ { type: 'confirm', name: 'move', - message: `Do you want to move ${count} messages?`, + message: `Do you want to move ${moveCount} of ${count} messages?`, default: false, }, ]); @@ -29,12 +36,12 @@ const handle = async ({ } } - const spinner = new Spinner(`Moving ${count} messages...`); + const spinner = new Spinner(`Moving ${moveCount} messages...`); spinner.start(); const promises = []; - for (let i = 0; i < count; i += 1) { + for (let i = 0; i < moveCount; i += 1) { promises.push(sqs.moveMessage(sourceQueueUrl, targetQueueUrl)); } @@ -45,7 +52,7 @@ const handle = async ({ throw new Error(e.message); }); - return count; + return moveCount; }; module.exports = { From f11782c30d5522482429c48430abbb8258fb5dc5 Mon Sep 17 00:00:00 2001 From: Troy Jones Date: Wed, 13 Oct 2021 07:38:31 -0600 Subject: [PATCH 3/5] Add copy option - messages are not deleted from source queue - resolves #23 --- src/index.js | 11 +++++++++- src/main.js | 10 +++++++-- src/main.spec.js | 56 +++++++++++++++++++++++++++++++++++++++--------- src/sqs.js | 6 ++++-- src/sqs.spec.js | 26 ++++++++++++++++++++++ 5 files changed, 94 insertions(+), 15 deletions(-) diff --git a/src/index.js b/src/index.js index 9d6d313..eac6c57 100644 --- a/src/index.js +++ b/src/index.js @@ -30,11 +30,18 @@ const toQuestion = { const handleAction = (from, to, options) => { const questions = []; + let copy = false; + let copiedOrMoved = 'moved'; if (!options.region) { questions.push(regionQuestion); } + if (options.copy) { + copy = true; + copiedOrMoved = 'copied'; + } + if (!from) { questions.push(fromQuestion); } @@ -57,6 +64,7 @@ const handleAction = (from, to, options) => { count = await handle({ sourceQueueUrl, targetQueueUrl, + copy, sqs, prompt, skipPrompt: options.yes, @@ -66,7 +74,7 @@ const handleAction = (from, to, options) => { process.exit(1); } - console.log(`${count} messages moved from ${sourceQueueUrl} to ${targetQueueUrl}!`); + console.log(`${count} messages ${copiedOrMoved} from ${sourceQueueUrl} to ${targetQueueUrl}!`); }); }; @@ -74,5 +82,6 @@ program .arguments('[from] [to]') .option('-r, --region [value]', 'The AWS region') .option('-y, --yes', 'Non interactive message moving') + .option('-c, --copy', 'Copy messages to new queue, do not delete') .action(handleAction) .parse(process.argv); diff --git a/src/main.js b/src/main.js index 9e84174..b04fc36 100644 --- a/src/main.js +++ b/src/main.js @@ -3,6 +3,7 @@ const { Spinner } = require('clui'); const handle = async ({ sourceQueueUrl, targetQueueUrl, + copy, sqs, prompt, skipPrompt, @@ -14,12 +15,17 @@ const handle = async ({ throw new Error(`The queue ${sourceQueueUrl} is empty!`); } + let copyOrMove = 'move'; + if (copy) { + copyOrMove = 'copy'; + } + if (!skipPrompt) { const { move } = await prompt([ { type: 'confirm', name: 'move', - message: `Do you want to move ${count} messages?`, + message: `Do you want to ${copyOrMove} ${count} messages?`, default: false, }, ]); @@ -35,7 +41,7 @@ const handle = async ({ const promises = []; for (let i = 0; i < count; i += 1) { - promises.push(sqs.moveMessage(sourceQueueUrl, targetQueueUrl)); + promises.push(sqs.moveMessage(sourceQueueUrl, targetQueueUrl, copy)); } await Promise.all(promises).then(() => { diff --git a/src/main.spec.js b/src/main.spec.js index 3373f66..44e94f9 100644 --- a/src/main.spec.js +++ b/src/main.spec.js @@ -5,6 +5,9 @@ const { handle } = require('./main'); describe('handle', () => { + const sourceQueueUrl = 'https://sqs.region.amazonaws.com/123456789/srcQueue'; + const targetQueueUrl = 'https://sqs.region.amazonaws.com/123456789/targetQueue'; + test('to move messages', async () => { const sqs = { getCount: jest.fn(() => 3), @@ -12,15 +15,39 @@ describe('handle', () => { }; const prompt = jest.fn(() => ({ move: true })); + const copy = false; + + await handle({ + sourceQueueUrl: sourceQueueUrl, + targetQueueUrl: targetQueueUrl, + copy, + sqs, + prompt, + }); + + expect(sqs.getCount).toHaveBeenCalled(); + expect(sqs.moveMessage).toHaveBeenCalledWith(sourceQueueUrl, targetQueueUrl, copy); + }); + + test('to copy messages', async () => { + const sqs = { + getCount: jest.fn(() => 3), + moveMessage: jest.fn(), + }; + + const prompt = jest.fn(() => ({ move: true })); + const copy = true; await handle({ - sourceQueueUrl: 'https://sqs.region.amazonaws.com/123456789/srcQueue', - targetQueueUrl: 'https://sqs.region.amazonaws.com/123456789/targetQueue', + sourceQueueUrl, + targetQueueUrl, + copy, sqs, prompt, }); expect(sqs.getCount).toHaveBeenCalled(); + expect(sqs.moveMessage).toHaveBeenCalledWith(sourceQueueUrl, targetQueueUrl, copy); }); test('to move messages without prompt', async () => { @@ -30,16 +57,19 @@ describe('handle', () => { }; const prompt = jest.fn(); + const copy = false; await handle({ - sourceQueueUrl: 'https://sqs.region.amazonaws.com/123456789/srcQueue', - targetQueueUrl: 'https://sqs.region.amazonaws.com/123456789/targetQueue', + sourceQueueUrl, + targetQueueUrl, + copy, sqs, prompt, skipPrompt: true, }); expect(sqs.getCount).toHaveBeenCalled(); + expect(sqs.moveMessage).toHaveBeenCalledWith(sourceQueueUrl, targetQueueUrl, copy); }); describe('reject promise', () => { @@ -50,10 +80,12 @@ describe('handle', () => { }; const prompt = jest.fn(() => ({ move: true })); + const copy = false; expect(handle({ - sourceQueueUrl: 'https://sqs.region.amazonaws.com/123456789/srcQueue', - targetQueueUrl: 'https://sqs.region.amazonaws.com/123456789/targetQueue', + sourceQueueUrl, + targetQueueUrl, + copy, sqs, prompt, })).rejects.toEqual({ message: 'getCount' }); @@ -66,10 +98,12 @@ describe('handle', () => { }; const prompt = jest.fn(() => ({ move: true })); + const copy = false; expect(handle({ - sourceQueueUrl: 'https://sqs.region.amazonaws.com/123456789/srcQueue', - targetQueueUrl: 'https://sqs.region.amazonaws.com/123456789/targetQueue', + sourceQueueUrl, + targetQueueUrl, + copy, sqs, prompt, })).rejects.toEqual(new Error('moveMessage')); @@ -82,10 +116,12 @@ describe('handle', () => { }; const prompt = jest.fn(() => ({ move: true })); + const copy = false; expect(handle({ - sourceQueueUrl: 'https://sqs.region.amazonaws.com/123456789/srcQueue', - targetQueueUrl: 'https://sqs.region.amazonaws.com/123456789/targetQueue', + sourceQueueUrl, + targetQueueUrl, + copy, sqs, prompt, })).rejects.toEqual(new Error('The queue https://sqs.region.amazonaws.com/123456789/srcQueue is empty!')); diff --git a/src/sqs.js b/src/sqs.js index 6cb41b7..79f1693 100644 --- a/src/sqs.js +++ b/src/sqs.js @@ -36,7 +36,7 @@ const createClient = (sqs) => { ); }); - const moveMessage = (sourceQueueUrl, targetQueueUrl) => ( + const moveMessage = (sourceQueueUrl, targetQueueUrl, copy) => ( new Promise(async (resolve, reject) => { try { const receivedMessage = await receiveMessage(sourceQueueUrl); @@ -48,7 +48,9 @@ const createClient = (sqs) => { const { Body, ReceiptHandle } = receivedMessage; await sendMessage(targetQueueUrl, Body); - await deleteMessage(sourceQueueUrl, ReceiptHandle); + if (!copy) { + await deleteMessage(sourceQueueUrl, ReceiptHandle); + } resolve(ReceiptHandle); } catch (error) { diff --git a/src/sqs.spec.js b/src/sqs.spec.js index 8c20373..4d80a7f 100644 --- a/src/sqs.spec.js +++ b/src/sqs.spec.js @@ -73,6 +73,28 @@ describe('moveMessage', () => { const handle = await sqs.moveMessage( 'https://sqs.region.amazonaws.com/123456789/srcQueue', 'https://sqs.region.amazonaws.com/123456789/targetQueue', + false, + ); + expect(handle).toEqual('ReceiptHandle'); + }); + + test('to resolve promise - copy', async () => { + const sqs = createClient({ + receiveMessage: mockCallbackFunction(null, { + Messages: [ + { + Body: 'Body', + ReceiptHandle: 'ReceiptHandle', + }, + ], + }), + sendMessage: mockCallbackFunction(null, {}), + }); + + const handle = await sqs.moveMessage( + 'https://sqs.region.amazonaws.com/123456789/srcQueue', + 'https://sqs.region.amazonaws.com/123456789/targetQueue', + true, ); expect(handle).toEqual('ReceiptHandle'); }); @@ -87,6 +109,7 @@ describe('moveMessage', () => { expect(sqs.moveMessage( 'https://sqs.region.amazonaws.com/123456789/srcQueue', 'https://sqs.region.amazonaws.com/123456789/targetQueue', + false, )).rejects.toEqual({ message: 'error' }); }); @@ -110,6 +133,7 @@ describe('moveMessage', () => { await sqs.moveMessage( 'https://sqs.region.amazonaws.com/123456789/srcQueue', 'https://sqs.region.amazonaws.com/123456789/targetQueue', + false, ); expect(mockSqs.receiveMessage).toHaveBeenCalledWith( { QueueUrl: 'https://sqs.region.amazonaws.com/123456789/srcQueue' }, @@ -137,6 +161,7 @@ describe('moveMessage', () => { await sqs.moveMessage( 'https://sqs.region.amazonaws.com/123456789/srcQueue', 'https://sqs.region.amazonaws.com/123456789/targetQueue', + false, ); expect(mockSqs.sendMessage).toHaveBeenCalledWith( { @@ -167,6 +192,7 @@ describe('moveMessage', () => { await sqs.moveMessage( 'https://sqs.region.amazonaws.com/123456789/srcQueue', 'https://sqs.region.amazonaws.com/123456789/targetQueue', + false, ); expect(mockSqs.deleteMessage).toHaveBeenCalledWith( { From 98c24205367a3ac1b70d376c43718a091d0c4f48 Mon Sep 17 00:00:00 2001 From: Troy Jones Date: Wed, 13 Oct 2021 08:02:39 -0600 Subject: [PATCH 4/5] Add unit tests, cleanup with lint --- package.json | 1 + src/helper.js | 6 ++--- src/helper.spec.js | 29 +++++++++++++++++++++++- src/index.js | 6 ++--- src/main.js | 12 +++++----- src/main.spec.js | 56 ++++++++++++++++++++++++++++++++++++---------- 6 files changed, 85 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index 44d2983..0c69d7e 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "license": "MIT", "scripts": { "lint": "eslint . --cache", + "lint:fix": "eslint . --fix --cache", "test": "jest", "test:watch": "yarn test --watch --onlyChanged", "test:coverage": "yarn test --coverage", diff --git a/src/helper.js b/src/helper.js index eef8dd7..4f24c50 100644 --- a/src/helper.js +++ b/src/helper.js @@ -9,13 +9,13 @@ const validateUrl = (input) => { const validateMaxMessages = (input) => { let valid = true; if (!input.match(/^[1-9][0-9]*$/)) { - valid = 'Please enter a valid max number of messages greater than 0!' + valid = 'Please enter a valid max number of messages greater than 0!'; } return valid; -} +}; module.exports = { validateUrl, - validateMaxMessages + validateMaxMessages, }; diff --git a/src/helper.spec.js b/src/helper.spec.js index 2191218..827521a 100644 --- a/src/helper.spec.js +++ b/src/helper.spec.js @@ -2,7 +2,7 @@ * @jest-environment node */ -const { validateUrl } = require('./helper'); +const { validateUrl, validateMaxMessages } = require('./helper'); describe('validateUrl', () => { test('to pass invalid url', () => { @@ -20,3 +20,30 @@ describe('validateUrl', () => { expect(validate).toBe(true); }); }); + +describe('validateMaxMessages', () => { + test('to pass an invalid value with numeric and alpha chars', () => { + const validate = validateMaxMessages('1abc'); + expect(validate).toEqual('Please enter a valid max number of messages greater than 0!'); + }); + + test('to pass an invalid value with only alpha chars', () => { + const validate = validateMaxMessages('abc'); + expect(validate).toEqual('Please enter a valid max number of messages greater than 0!'); + }); + + test('to pass an empty value', () => { + const validate = validateMaxMessages(''); + expect(validate).toEqual('Please enter a valid max number of messages greater than 0!'); + }); + + test('to pass negative number', () => { + const validate = validateMaxMessages('-1'); + expect(validate).toEqual('Please enter a valid max number of messages greater than 0!'); + }); + + test('to pass valid number', () => { + const validate = validateMaxMessages('10'); + expect(validate).toBe(true); + }); +}); diff --git a/src/index.js b/src/index.js index 2349903..cf22be9 100644 --- a/src/index.js +++ b/src/index.js @@ -33,7 +33,7 @@ const maxQuestion = { name: 'maxMessages', message: 'Enter the max number of messages:', validate: validateMaxMessages, -} +}; const handleAction = (from, to, options) => { const questions = []; @@ -43,7 +43,7 @@ const handleAction = (from, to, options) => { } if (!options.maxMessages) { - questions.push(maxQuestion) + questions.push(maxQuestion); } if (!from) { @@ -67,9 +67,9 @@ const handleAction = (from, to, options) => { try { count = await handle({ - maxMessages, sourceQueueUrl, targetQueueUrl, + maxMessages, sqs, prompt, skipPrompt: options.yes, diff --git a/src/main.js b/src/main.js index b01d782..2a6518f 100644 --- a/src/main.js +++ b/src/main.js @@ -1,9 +1,9 @@ const { Spinner } = require('clui'); const handle = async ({ - maxMessages, sourceQueueUrl, targetQueueUrl, + maxMessages, sqs, prompt, skipPrompt, @@ -11,14 +11,14 @@ const handle = async ({ const count = await sqs.getCount(sourceQueueUrl); await sqs.getCount(targetQueueUrl); - if (parseInt(count) === 0) { + if (parseInt(count, 10) === 0) { throw new Error(`The queue ${sourceQueueUrl} is empty!`); } - maxMessages = parseInt(maxMessages); - moveCount = count - if(count > maxMessages) { - moveCount = maxMessages + const maxCount = parseInt(maxMessages, 10); + let moveCount = count; + if (count > maxCount) { + moveCount = maxCount; } if (!skipPrompt) { diff --git a/src/main.spec.js b/src/main.spec.js index 3373f66..dc8de82 100644 --- a/src/main.spec.js +++ b/src/main.spec.js @@ -5,35 +5,64 @@ const { handle } = require('./main'); describe('handle', () => { + const sourceQueueUrl = 'https://sqs.region.amazonaws.com/123456789/srcQueue'; + const targetQueueUrl = 'https://sqs.region.amazonaws.com/123456789/targetQueue'; + const maxMessages = 5; + let getCountVal = 3; + test('to move messages', async () => { const sqs = { - getCount: jest.fn(() => 3), + getCount: jest.fn(() => getCountVal), + moveMessage: jest.fn(), + }; + + const prompt = jest.fn(() => ({ move: true })); + + await handle({ + sourceQueueUrl, + targetQueueUrl, + maxMessages, + sqs, + prompt, + }); + + expect(sqs.getCount).toHaveBeenCalled(); + expect(sqs.moveMessage).toHaveBeenCalledTimes(getCountVal); + }); + + test('to move messages with maxCount', async () => { + const sqs = { + getCount: jest.fn(() => getCountVal), moveMessage: jest.fn(), }; const prompt = jest.fn(() => ({ move: true })); + getCountVal = 10; await handle({ - sourceQueueUrl: 'https://sqs.region.amazonaws.com/123456789/srcQueue', - targetQueueUrl: 'https://sqs.region.amazonaws.com/123456789/targetQueue', + sourceQueueUrl, + targetQueueUrl, + maxMessages, sqs, prompt, }); expect(sqs.getCount).toHaveBeenCalled(); + expect(sqs.moveMessage).toHaveBeenCalledTimes(maxMessages); }); test('to move messages without prompt', async () => { const sqs = { - getCount: jest.fn(() => 3), + getCount: jest.fn(() => getCountVal), moveMessage: jest.fn(), }; const prompt = jest.fn(); await handle({ - sourceQueueUrl: 'https://sqs.region.amazonaws.com/123456789/srcQueue', - targetQueueUrl: 'https://sqs.region.amazonaws.com/123456789/targetQueue', + sourceQueueUrl, + targetQueueUrl, + maxMessages, sqs, prompt, skipPrompt: true, @@ -52,8 +81,9 @@ describe('handle', () => { const prompt = jest.fn(() => ({ move: true })); expect(handle({ - sourceQueueUrl: 'https://sqs.region.amazonaws.com/123456789/srcQueue', - targetQueueUrl: 'https://sqs.region.amazonaws.com/123456789/targetQueue', + sourceQueueUrl, + targetQueueUrl, + maxMessages, sqs, prompt, })).rejects.toEqual({ message: 'getCount' }); @@ -68,8 +98,9 @@ describe('handle', () => { const prompt = jest.fn(() => ({ move: true })); expect(handle({ - sourceQueueUrl: 'https://sqs.region.amazonaws.com/123456789/srcQueue', - targetQueueUrl: 'https://sqs.region.amazonaws.com/123456789/targetQueue', + sourceQueueUrl, + targetQueueUrl, + maxMessages, sqs, prompt, })).rejects.toEqual(new Error('moveMessage')); @@ -84,8 +115,9 @@ describe('handle', () => { const prompt = jest.fn(() => ({ move: true })); expect(handle({ - sourceQueueUrl: 'https://sqs.region.amazonaws.com/123456789/srcQueue', - targetQueueUrl: 'https://sqs.region.amazonaws.com/123456789/targetQueue', + sourceQueueUrl, + targetQueueUrl, + maxMessages, sqs, prompt, })).rejects.toEqual(new Error('The queue https://sqs.region.amazonaws.com/123456789/srcQueue is empty!')); From 9cd0f4470872627ce7d8a2d4a42894021ed66184 Mon Sep 17 00:00:00 2001 From: Troy Jones Date: Wed, 13 Oct 2021 09:57:22 -0600 Subject: [PATCH 5/5] Add/fix unit tests and lint errors --- package.json | 1 + src/sqs.js | 16 +++---- src/sqs.spec.js | 125 ++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 108 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index 44d2983..0c69d7e 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "license": "MIT", "scripts": { "lint": "eslint . --cache", + "lint:fix": "eslint . --fix --cache", "test": "jest", "test:watch": "yarn test --watch --onlyChanged", "test:coverage": "yarn test --coverage", diff --git a/src/sqs.js b/src/sqs.js index 009ac95..f7c175a 100644 --- a/src/sqs.js +++ b/src/sqs.js @@ -1,8 +1,8 @@ const createClient = (sqs) => { - const sendMessage = (QueueUrl, MessageBody, MessageAttributes) => new Promise((resolve, reject) => { + const sendMessage = (QueueUrl, MessageBody, MessageAttributes) => new Promise((resolve, rej) => { sqs.sendMessage( { QueueUrl, MessageBody, MessageAttributes }, - (error, data) => (error ? reject(error) : resolve(data)), + (error, data) => (error ? rej(error) : resolve(data)), ); }); @@ -45,15 +45,15 @@ const createClient = (sqs) => { throw 'Queue is empty'; // eslint-disable-line } - const { Body, ReceiptHandle } = receivedMessage; - var { MessageAttributes } = receivedMessage; + const { Body, ReceiptHandle, MessageAttributes } = receivedMessage; const deleteArrays = (obj) => { - if (typeof obj !== "undefined") { - Object.keys(obj).forEach(key => { + if (typeof obj !== 'undefined') { + Object.keys(obj).forEach((key) => { if (Array.isArray(obj[key])) { - delete obj[key]; // Remove invalid Array values in MessageAttributes - } else if (typeof obj[key] === "object") { + // Remove invalid Array values in MessageAttributes + delete obj[key]; // eslint-disable-line + } else if (typeof obj[key] === 'object') { deleteArrays(obj[key]); } }); diff --git a/src/sqs.spec.js b/src/sqs.spec.js index 8c20373..101f9fc 100644 --- a/src/sqs.spec.js +++ b/src/sqs.spec.js @@ -10,6 +10,9 @@ const mockCallbackFunction = (error, data) => ( } ); +const sourceQueueUrl = 'https://sqs.region.amazonaws.com/123456789/srcQueue'; +const targetQueueUrl = 'https://sqs.region.amazonaws.com/123456789/targetQueue'; + describe('getCount', () => { test('to resolve promise', async () => { const sqs = createClient({ @@ -19,7 +22,7 @@ describe('getCount', () => { ), }); - const count = await sqs.getCount('https://sqs.region.amazonaws.com/123456789/queue'); + const count = await sqs.getCount(sourceQueueUrl); expect(count).toEqual(3); }); @@ -28,7 +31,7 @@ describe('getCount', () => { getQueueAttributes: mockCallbackFunction({ message: 'error' }, null), }); - expect(sqs.getCount('https://sqs.region.amazonaws.com/123456789/queue')).rejects.toEqual({ message: 'error' }); + expect(sqs.getCount(sourceQueueUrl)).rejects.toEqual({ message: 'error' }); }); test('to pass queue url', async () => { @@ -42,10 +45,10 @@ describe('getCount', () => { const sqs = createClient(mockSqs); - await sqs.getCount('https://sqs.region.amazonaws.com/123456789/queue'); + await sqs.getCount(sourceQueueUrl); expect(mockSqs.getQueueAttributes).toHaveBeenCalledWith( { - QueueUrl: 'https://sqs.region.amazonaws.com/123456789/queue', + QueueUrl: sourceQueueUrl, AttributeNames: [ 'ApproximateNumberOfMessages', ], @@ -63,6 +66,7 @@ describe('moveMessage', () => { { Body: 'Body', ReceiptHandle: 'ReceiptHandle', + MessageAttributes: 'MessageAttributes', }, ], }), @@ -70,10 +74,7 @@ describe('moveMessage', () => { deleteMessage: mockCallbackFunction(null, {}), }); - const handle = await sqs.moveMessage( - 'https://sqs.region.amazonaws.com/123456789/srcQueue', - 'https://sqs.region.amazonaws.com/123456789/targetQueue', - ); + const handle = await sqs.moveMessage(sourceQueueUrl, targetQueueUrl); expect(handle).toEqual('ReceiptHandle'); }); @@ -84,10 +85,7 @@ describe('moveMessage', () => { deleteMessage: mockCallbackFunction(null, {}), }); - expect(sqs.moveMessage( - 'https://sqs.region.amazonaws.com/123456789/srcQueue', - 'https://sqs.region.amazonaws.com/123456789/targetQueue', - )).rejects.toEqual({ message: 'error' }); + expect(sqs.moveMessage(sourceQueueUrl, targetQueueUrl)).rejects.toEqual({ message: 'error' }); }); test('to call receiveMessage with expected parameters', async () => { @@ -107,23 +105,25 @@ describe('moveMessage', () => { const sqs = createClient(mockSqs); - await sqs.moveMessage( - 'https://sqs.region.amazonaws.com/123456789/srcQueue', - 'https://sqs.region.amazonaws.com/123456789/targetQueue', - ); + await sqs.moveMessage(sourceQueueUrl, targetQueueUrl); expect(mockSqs.receiveMessage).toHaveBeenCalledWith( - { QueueUrl: 'https://sqs.region.amazonaws.com/123456789/srcQueue' }, + { + QueueUrl: sourceQueueUrl, + MessageAttributeNames: ['All'], + }, expect.any(Function), ); }); test('to call sendMessage with expected parameters', async () => { + const messageAttributes = 'MessageAttributes'; const mockSqs = { receiveMessage: mockCallbackFunction(null, { Messages: [ { Body: 'Body', ReceiptHandle: 'ReceiptHandle', + MessageAttributes: messageAttributes, }, ], }), @@ -134,14 +134,90 @@ describe('moveMessage', () => { const sqs = createClient(mockSqs); - await sqs.moveMessage( - 'https://sqs.region.amazonaws.com/123456789/srcQueue', - 'https://sqs.region.amazonaws.com/123456789/targetQueue', + await sqs.moveMessage(sourceQueueUrl, targetQueueUrl); + expect(mockSqs.sendMessage).toHaveBeenCalledWith( + { + MessageBody: 'Body', + QueueUrl: targetQueueUrl, + MessageAttributes: messageAttributes, + }, + expect.any(Function), ); + }); + + test('to call sendMessage with MessageAttributes', async () => { + const messageAttributes = { + messageType: { + Type: 'String', + StringValue: 'messageType1', + }, + }; + const mockSqs = { + receiveMessage: mockCallbackFunction(null, { + Messages: [ + { + Body: 'Body', + ReceiptHandle: 'ReceiptHandle', + MessageAttributes: messageAttributes, + }, + ], + }), + sendMessage: mockCallbackFunction(null, {}), + deleteMessage: mockCallbackFunction(null, {}), + }; + jest.spyOn(mockSqs, 'sendMessage'); + + const sqs = createClient(mockSqs); + + await sqs.moveMessage(sourceQueueUrl, targetQueueUrl); expect(mockSqs.sendMessage).toHaveBeenCalledWith( { MessageBody: 'Body', - QueueUrl: 'https://sqs.region.amazonaws.com/123456789/targetQueue', + QueueUrl: targetQueueUrl, + MessageAttributes: messageAttributes, + }, + expect.any(Function), + ); + }); + + test('to call sendMessage with MessageAttributes adjusted', async () => { + const expectedMessageAttributes = { + messageType: { + Type: 'String', + StringValue: 'messageType1', + }, + }; + + const messageAttributes = { + messageType: { + Type: 'String', + StringValue: 'messageType1', + }, + }; + + const mockSqs = { + receiveMessage: mockCallbackFunction(null, { + Messages: [ + { + Body: 'Body', + ReceiptHandle: 'ReceiptHandle', + MessageAttributes: messageAttributes, + }, + ], + }), + sendMessage: mockCallbackFunction(null, {}), + deleteMessage: mockCallbackFunction(null, {}), + }; + jest.spyOn(mockSqs, 'sendMessage'); + + const sqs = createClient(mockSqs); + + await sqs.moveMessage(sourceQueueUrl, targetQueueUrl); + expect(mockSqs.sendMessage).toHaveBeenCalledWith( + { + MessageBody: 'Body', + QueueUrl: targetQueueUrl, + MessageAttributes: expectedMessageAttributes, }, expect.any(Function), ); @@ -164,14 +240,11 @@ describe('moveMessage', () => { const sqs = createClient(mockSqs); - await sqs.moveMessage( - 'https://sqs.region.amazonaws.com/123456789/srcQueue', - 'https://sqs.region.amazonaws.com/123456789/targetQueue', - ); + await sqs.moveMessage(sourceQueueUrl, targetQueueUrl); expect(mockSqs.deleteMessage).toHaveBeenCalledWith( { ReceiptHandle: 'ReceiptHandle', - QueueUrl: 'https://sqs.region.amazonaws.com/123456789/srcQueue', + QueueUrl: sourceQueueUrl, }, expect.any(Function), );