こんにちは最近、 serverless ✕ DynamoDB ✕ node.js の組み合わせでのサービス開発を
自分の中のメイン方式にするべく習得中です。
2週間くらい経つと学んだことを忘れてしまうので、
重宝した参考記事と書いた設定/コードを備忘のため、チートシートとしてまとめます。
本記事で書くこと
- ①serverless.yml でのDynamoDBテーブル定義例
- ②node.jsのAWS SDK「DynamoDB Client」の使用例
- ③node.js ローカル環境でのテスト記述例
前提: 題材としたシステムについて
家族とシェアしている Google Photos の写真をLINE Botで検索、
懐かしの思い出の写真を自動投稿 する、ほのぼの系Botを作っています。
Room/Album/Photoの3つのテーブルを例に説明します。
テーブル:「Room」 BOTとの会話部屋情報を管理
- PrimaryKey は
roomId
- SortKey はなしです。
room.json
{
"roomId": "room1",
"albumKeyword": "keyword1",
"roomType": "user",
"users": [{
"userId": "user1",
"googleRefreshToken": "token1"
}]
}
テーブル:「Album」 写真アルバム情報を管理
- PrimaryKey は
roomId
- SortKey は
albumId
album.json
{
"roomId": "room1",
"albumId": "album1",
"albumTitle": "AlbumName1 keyword1",
"numPhotos": 3,
"status": "WAITING_UPDATE"
}
テーブル:「Photo」 写真情報を管理
- PrimaryKey は
roomId
- SortKey が
albumIKey
(albumId と photoId の複合キー)- albumIDをキーに写真検索したかったので。アルバム削除時に写真も削除したいため。
- LocalSecondaryIndexに
date
を指定- 日付で写真検索したかったので。
photo.json
{
"roomId": "room1",
"albumIdKey": "album1-photo1",
"date": "20170201",
"timestamp": "1485955237000",
"type": "image/jpeg",
"url": "https://lh3.googleusercontent.com/xxx/xxx/xxx/a1-p1.JPG"
}
①serverless.yml でのDynamoDBテーブル定義例
参考記事
以下の記事にお世話になりました。
- AWS+Serverless+DynamoDB+APIGateway+Lambdaでアプリ用サーバーAPIを簡単?に作成する
- Serverless アプリケーションをローカルで開発する
- gistに置かれていたserverless.ymlの定義例
書いた設定ファイル
serverless.yml
# ...略...
custom:
defaultStage: dev
tableNameRoom: '${self:service}-Room-${self:provider.stage}'
tableNameAlbum: '${self:service}-Album-${self:provider.stage}'
tableNamePhoto: '${self:service}-Photo-${self:provider.stage}'
dynamodb:
start:
port: 8000
inMemory: true
migrate: true
seed: true
seed:
development:
sources:
- table: ${self:custom.tableNameRoom}
sources: [./migrations/rooms.json]
- table: ${self:custom.tableNameAlbum}
sources: [./migrations/albums.json]
- table: ${self:custom.tableNamePhoto}
sources: [./migrations/photos.json]
resources:
Resources:
RoomTable:
Type: 'AWS::DynamoDB::Table'
Properties:
TableName: ${self:custom.tableNameRoom}
AttributeDefinitions:
- AttributeName: roomId
AttributeType: S
KeySchema:
- AttributeName: roomId
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
AlbumTable:
Type: 'AWS::DynamoDB::Table'
Properties:
TableName: ${self:custom.tableNameAlbum}
AttributeDefinitions:
- AttributeName: roomId
AttributeType: S
- AttributeName: albumId
AttributeType: S
KeySchema:
- AttributeName: roomId
KeyType: HASH
- AttributeName: albumId
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
PhotoTable:
Type: 'AWS::DynamoDB::Table'
Properties:
TableName: ${self:custom.tableNamePhoto}
AttributeDefinitions:
- AttributeName: roomId
AttributeType: S
- AttributeName: albumIdKey
AttributeType: S
- AttributeName: date
AttributeType: S
KeySchema:
- AttributeName: roomId
KeyType: HASH
- AttributeName: albumIdKey
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
LocalSecondaryIndexes:
- IndexName: dateIndex
KeySchema:
- AttributeName: roomId
KeyType: HASH
- AttributeName: date
KeyType: RANGE
Projection:
ProjectionType: ALL
②node.jsのAWS SDK「DynamoDB Client」の使い方
参考記事
-
【詳解】JavascriptでDynamoDBを操作する
- ものすごく具体的かつわかりやすくまとまっており、大変参考になりました。
- AWS公式リファレンス - Class: AWS.DynamoDB.DocumentClient
書いたコード (table毎のDynamoDB操作をモジュール化)
dynamodb.js
'use strict';
const AWS = require('aws-sdk');
class DynamoDBClient {
constructor(options) {
this.docClient = new AWS.DynamoDB.DocumentClient(options);
this.tableNameRoom = options.tableNameRoom;
this.tableNameAlbum = options.tableNameAlbum;
this.tableNamePhoto = options.tableNamePhoto;
}
async scanRoom() {
const params = {
TableName: this.tableNameRoom
};
return this.docClient.scan(params).promise();
}
async getRoomItem(roomId) {
const params = {
TableName: this.tableNameRoom,
Key: {
roomId: roomId
}
};
return this.docClient.get(params).promise();
}
async getAlbums(roomId) {
const params = {
TableName: this.tableNameAlbum,
KeyConditionExpression: '#pk = :pk_value',
ExpressionAttributeNames: {
'#pk': 'roomId',
},
ExpressionAttributeValues: {
':pk_value': roomId,
}
};
return this.docClient.query(params).promise();
}
async getAlbumItem(roomId, albumId) {
const params = {
TableName: this.tableNameAlbum,
Key: {
roomId: roomId,
albumId: albumId
}
};
return this.docClient.get(params).promise();
}
async getPhotosByAlbumId(roomId, albumId) {
const params = {
TableName: this.tableNamePhoto,
KeyConditionExpression: '#pk = :pk_value AND begins_with(#sk, :sk_value)',
ExpressionAttributeNames: {
'#pk': 'roomId',
'#sk': 'albumIdKey'
},
ExpressionAttributeValues: {
':pk_value': roomId,
':sk_value': albumId
}
};
return this.docClient.query(params).promise();
}
async getPhotosByDate(roomId, dateStr) {
const params = {
TableName: this.tableNamePhoto,
IndexName: 'dateIndex',
KeyConditionExpression: '#pk = :pk_value AND begins_with(#sk, :sk_value)',
ExpressionAttributeNames: {
'#pk': 'roomId',
'#sk': 'date'
},
ExpressionAttributeValues: {
':pk_value': roomId,
':sk_value': dateStr
}
};
return this.docClient.query(params).promise();
};
async putRoomItem(room) {
const params = {
TableName: this.tableNameRoom,
Item: room
};
return this.docClient.put(params).promise();
}
async updateRoomItemUsers(roomId, users) {
const params = {
TableName: this.tableNameRoom,
Key: {
roomId: roomId
},
ExpressionAttributeNames: {
'#users': 'users'
},
ExpressionAttributeValues: {
':newUsers': users
},
UpdateExpression: 'SET #users = :newUsers'
};
return this.docClient.update(params).promise();
}
async putAlbumItem(album) {
const params = {
TableName: this.tableNameAlbum,
Item: album
};
return this.docClient.put(params).promise();
}
async putPhotoItem(photo) {
const params = {
TableName: this.tableNamePhoto,
Item: photo
};
return this.docClient.put(params).promise();
}
async deleteRoomItem(roomId) {
const params = {
TableName: this.tableNameRoom,
Key: {
roomId: roomId
},
};
return this.docClient.delete(params).promise();
}
async deleteAlbumItem(roomId, albumId) {
const params = {
TableName: this.tableNameAlbum,
Key: {
roomId: roomId,
albumId: albumId
}
};
return this.docClient.delete(params).promise();
}
async deletePhotoItem(roomId, albumIdKey) {
const params = {
TableName: this.tableNamePhoto,
Key: {
roomId: roomId,
albumIdKey: albumIdKey
}
};
return this.docClient.delete(params).promise();
}
}
module.exports = DynamoDBClient;
③node.js ローカル環境でのテストの書き方
参考記事
書いたコード (Dynamo.jsに対するテストコード)
dynamodb-test.js
'use strict';
const _ = require('underscore');
const chai = require('chai');
const assert = chai.assert;
const TABLE_NAME_ROOM = 'YOUR_SERVICE_NAME-Room-dev';
const TABLE_NAME_ALBUM = 'YOUR_SERVICE_NAME-Album-dev';
const TABLE_NAME_PHOTO = 'YOUR_SERVICE_NAME-Photo-dev';
const DynamoClient = require('./../../lib/dynamodb');
const client = new DynamoClient({
region: 'localhost',
endpoint: 'http://localhost:8000',
tableNameRoom: TABLE_NAME_ROOM,
tableNameAlbum: TABLE_NAME_ALBUM,
tableNamePhoto: TABLE_NAME_PHOTO
});
describe('check DynamoDB Client using local DynamoDB service', () => {
it('should get all room items from local DynamoDB by scanRoom', async() => {
const result = await client.scanRoom();
assert.equal(result.Count, 2);
const rooms = require('./../../migrations/rooms.json');
const roomIds = rooms.map(room => room.roomId);
const albumKeywords = rooms.map(room => room.albumKeyword);
for (const item of result.Items) {
assert.include(roomIds, item.roomId);
assert.include(albumKeywords, item.albumKeyword);
}
});
it('should get a room item from local DynamoDB by getRoomItem', async() => {
const result1 = await client.getRoomItem('room1');
assert.equal(result1.Item.albumKeyword, 'keyword1');
assert.equal(result1.Item.roomType, 'user');
assert.lengthOf(result1.Item.users, 1);
const result2 = await client.getRoomItem('room2');
assert.equal(result2.Item.albumKeyword, 'keyword2');
assert.equal(result2.Item.roomType, 'room');
assert.lengthOf(result2.Item.users, 3);
// Pattern of No result.
const result3 = await client.getRoomItem('room3');
assert.deepStrictEqual(result3, {});
});
it('should get album items associated roomId from local DynamoDB by getAlbums', async() => {
const result1 = await client.getAlbums('room1');
assert.equal(result1.Count, 2);
const albumIds = result1.Items.map(item => item.albumId);
assert.include(albumIds, 'album1');
assert.include(albumIds, 'album2');
const result2 = await client.getAlbums('room2');
assert.equal(result2.Count, 1);
assert.equal(result2.Items[0].albumId, 'album2');
// Pattern of No result.
const result3 = await client.getAlbums('room3');
assert.equal(result3.Count, 0);
});
it('should get a album item from local DynamoDB by getAlbumItem', async() => {
const result1 = await client.getAlbumItem('room1', 'album1');
assert.equal(result1.Item.numPhotos, 3);
assert.equal(result1.Item.albumTitle, 'AlbumName1 keyword1');
assert.equal(result1.Item.status, 'WAITING_UPDATE');
const result2 = await client.getAlbumItem('room1', 'album2');
assert.equal(result2.Item.numPhotos, 2);
assert.equal(result2.Item.albumTitle, 'AlbumName2 keyword2');
assert.equal(result2.Item.status, 'UPDATED');
const result3 = await client.getAlbumItem('room2', 'album2');
assert.equal(result3.Item.roomId, 'room2');
assert.equal(result3.Item.albumId, 'album2');
// Pattern of No result.
const result4 = await client.getAlbumItem('room2', 'album1');
assert.deepStrictEqual(result4, {});
});
it('should get photos from local DynamoDB by getPhotosByAlbumId', async() => {
const result1 = await client.getPhotosByAlbumId('room1', 'album1');
assert.equal(result1.Count, 3);
const result1AlbumIdKeys = result1.Items.map(item => item.albumIdKey);
assert.include(result1AlbumIdKeys, 'album1-photo1');
assert.include(result1AlbumIdKeys, 'album1-photo2');
assert.include(result1AlbumIdKeys, 'album1-photo3');
const result2 = await client.getPhotosByAlbumId('room1', 'album2');
assert.equal(result2.Count, 2);
const result2AlbumIdKeys = result2.Items.map(item => item.albumIdKey);
assert.include(result2AlbumIdKeys, 'album2-photo1');
assert.include(result2AlbumIdKeys, 'album2-photo2');
const result3 = await client.getPhotosByAlbumId('room2', 'album2');
assert.equal(result3.Count, 1);
assert.equal(result3.Items[0].albumIdKey, 'album2-photo1');
assert.equal(result3.Items[0].url, 'https://lh3.googleusercontent.com/xxx/xxx/xxx/a2-p1.JPG');
// Pattern of No result.
const result4 = await client.getPhotosByAlbumId('room1', 'album3');
assert.equal(result4.Count, 0);
});
it('should get photos from local DynamoDB by getPhotosByDate', async() => {
const result1 = await client.getPhotosByDate('room1', '20170201');
assert.equal(result1.Count, 1);
assert.equal(result1.Items[0].albumIdKey, 'album1-photo1');
const result2 = await client.getPhotosByDate('room1', '20170205');
assert.equal(result2.Count, 3);
const albumIdKeys = result2.Items.map(item => item.albumIdKey);
assert.include(albumIdKeys, 'album1-photo3');
assert.include(albumIdKeys, 'album2-photo1');
assert.include(albumIdKeys, 'album2-photo2');
const result3 = await client.getPhotosByDate('room2', '20170205');
assert.equal(result3.Count, 1);
assert.equal(result3.Items[0].albumIdKey, 'album2-photo1');
// Pattern of No result.
const result4 = await client.getPhotosByDate('room1', '20170202');
assert.equal(result4.Count, 0);
});
it('should put a room item to local DynamoDB by putRoomItem', async() => {
// new item
const data = {
roomId: 'newRoom1',
albumKeyword: 'newKeyword1',
roomType: 'room',
users: [
{userId: 'user1', googleRefreshToken: 'token1'}
]
};
// refresh the table
await client.deleteRoomItem(data.roomId);
// confirm empty result before put the item
const emptyResult = await client.getRoomItem(data.roomId);
assert.deepStrictEqual(emptyResult, {});
// put the new item
await client.putRoomItem(data);
const afterPutResult = await client.getRoomItem(data.roomId);
assert.deepStrictEqual(afterPutResult.Item, data);
// update the item of users
const newUsers = data.users;
newUsers.push({userId: 'new-user1'});
await client.updateRoomItemUsers(data.roomId, newUsers);
const afterUpdateResult2 = await client.getRoomItem(data.roomId);
assert.deepStrictEqual(afterUpdateResult2.Item.users, newUsers);
// delete the item for re-test
await client.deleteRoomItem(data.roomId);
});
it('should put a room items to local DynamoDB by putAlbumItem', async() => {
// new item
const dataRoomId = 'newRoom1';
const dataAlbumId = 'newAlbum1';
const data = {
roomId: dataRoomId,
albumId: dataAlbumId,
albumTitle: 'AlbumName1 newKeyword1',
numPhotos: 1,
status: 'WAITING_UPDATE'
};
// refresh the table
await client.deleteAlbumItem(dataRoomId, dataAlbumId);
// confirm empty result before put the item
const emptyResult = await client.getAlbumItem(dataRoomId, dataAlbumId);
assert.deepStrictEqual(emptyResult, {});
// put the new item
await client.putAlbumItem(data);
const afterPutResult = await client.getAlbumItem(dataRoomId, dataAlbumId);
assert.deepStrictEqual(afterPutResult.Item, data);
// update the item
data.status = 'UPDATED';
await client.putAlbumItem(data);
const afterUpdateResult = await client.getAlbumItem(dataRoomId, dataAlbumId);
assert.deepStrictEqual(afterUpdateResult.Item, data);
// delete the item for re-test
await client.deleteAlbumItem(dataRoomId, dataAlbumId);
});
it('should put a photo items to local DynamoDB by putPhotoItem', async() => {
// new item
const dataRoomId = 'newRoom1';
const dataAlbumId = 'newAlbum1';
const dataPhotoId = 'newPhoto1';
const dataAlbumIdKey = `${dataAlbumId}-${dataPhotoId}`;
const data = {
roomId: dataRoomId,
albumIdKey: dataAlbumIdKey,
date: '20170203',
timestamp: '1486076126000',
type: 'image/jpeg',
url: `https://lh3.googleusercontent.com/xxx/xxx/xxx/${dataAlbumIdKey}.JPG`
};
// refresh the table
await client.deletePhotoItem(dataRoomId, dataAlbumIdKey);
// confirm empty result before put the item
const emptyResult = await client.getPhotosByAlbumId(dataRoomId, dataAlbumIdKey);
assert.equal(emptyResult.Count, 0);
// put the new item
await client.putPhotoItem(data);
const afterPutResult = await client.getPhotosByAlbumId(dataRoomId, dataAlbumIdKey);
assert.equal(afterPutResult.Count, 1);
assert.deepStrictEqual(afterPutResult.Items[0], data);
// delete the item for re-test
await client.deletePhotoItem(dataRoomId, dataAlbumIdKey);
});
});
その他テストコード (SQS)
実行コード (SQS)
sqs.js
'use strict';
const _ = require('underscore');
const AWS = require('aws-sdk');
const dateUtil = require('./date-util.js');
const MAX_NUMBER_OF_RECEIVE_MESSAGES = 10;
const RECEIVE_VISIBILITY_TIMEOUT = 10;
const newMessageTemplate = (roomId, photo) => {
return {
messageType: 'UPDATE',
roomId: roomId,
albumIdKey: `${photo.album_id}-${photo.id}`,
date: dateUtil.getDay(parseInt(photo.timestamp)),
timestamp: photo.timestamp,
type: photo.content.type,
url: photo.content.src
};
};
const deleteMessageTemplate = (roomId, photo) => {
return {
messageType: 'DELETE',
roomId: roomId,
albumIdKey: photo.albumIdKey,
};
};
const completeMessageTemplate = (roomId, albumId) => {
return {
messageType: 'COMPLETE',
roomId: roomId,
albumId: albumId,
};
};
class SQSClient {
constructor(options) {
this.sqs = new AWS.SQS(options);
this.defaultParams = {
QueueUrl: options.QueueUrl
};
}
// PRIVATE METHOD
enqueue(message) {
const params = _.extend({MessageBody: JSON.stringify(message)}, this.defaultParams);
return this.sqs.sendMessage(params).promise();
}
enqueueNewMessage(roomId, photo) {
return this.enqueue(newMessageTemplate(roomId, photo));
}
enqueueDeleteMessage(roomId, photo) {
return this.enqueue(deleteMessageTemplate(roomId, photo));
}
enqueueCompleteMessage(roomId, albumId) {
return this.enqueue(completeMessageTemplate(roomId, albumId));
}
// PRIVATE METHOD
async receiveMessageHandler(message, handler) {
await handler(message);
const params = _.extend({ReceiptHandle: message.ReceiptHandle}, this.defaultParams);
return this.sqs.deleteMessage(params).promise();
}
async receiveMessages(handler) {
const params = _.extend({
MaxNumberOfMessages: MAX_NUMBER_OF_RECEIVE_MESSAGES,
VisibilityTimeout: RECEIVE_VISIBILITY_TIMEOUT
}, this.defaultParams);
const response = await this.sqs.receiveMessage(params).promise();
if (!response.Messages) return;
return Promise.all(response.Messages.map(message => this.receiveMessageHandler(message, handler)));
}
}
module.exports = SQSClient;
テストコード (SQS)
sqs-test.js
'use strict';
const _ = require('underscore');
const chai = require('chai');
const assert = chai.assert;
const proxyquire = require('proxyquire');
// DUMMY RESPONSE of AWS.SQS.sendMessage
const dummyResponseSendMessage = {
ResponseMetadata: {RequestId: 'e41cc172-b12c-51b1-851f-9218a2a3ec24'},
MD5OfMessageBody: 'e3c27b4365ce1fb9f86d32e64102a304',
MessageId: '2a669cd3-983d-44d0-9761-d60fd3ba8fa5'
};
// DUMMY RESPONSE of AWS.SQS.receiveMessage
const dummyResponseReceiveMessage = {
ResponseMetadata: {RequestId: '74f00447-e65c-5b57-aab5-572df5c2a200'},
Messages: [
{
MessageId: '2a669cd3-983d-44d0-9761-d60fd3ba8fa5',
ReceiptHandle: 'AQEBQLUDuMah/rsKknwzxP9gEOv+sc8I9dC57v2QcgNZ/N0qSHlcyLw+W7H5gSF+4FtJ6KAv1w1wp7/Aoe48/vPGRDoORkS4JOMzJEqC2Ql+jh8SmKfpxAw8hNQmOEcLJpDKp14i3thXyKT0Ad9uQHmg7T06zDbl0I9LqIja9D52wqWXKElEPruL5ZVArxT46QmXk0u6bsPQePHXCculvba0Gy7MW0xEXMy5yIWXDZL5CYmLZjQ5pQvuyVohp0L0kFw/152eKemu3vPENnGYB4kS3rC8heu6/QMn3+ODqreQHcO7C7w3ciiSvACUKLnePeJJwY3XUXKBMnf+OnuJRUrXOnTtpsuShqALYgGOoR/sYBWN/aqLJsT0syFhIMByPLXXm9X5St4m/WshwmkgqFZdD+kPAQTVdpIXm3xmenDjjKU=',
MD5OfBody: 'e3c27b4365ce1fb9f86d32e64102a304',
Body: '{"messageType":"DELETE","roomId":"room-1","albumKey":"album-1-photo-1"}'
},
{
MessageId: 'fe1b2e28-9200-48b2-b2ff-5a82be53c359',
ReceiptHandle: 'AQEBwakNhLL9MPSsSml32FtjNUrG6aQeVozaWSRQYeH/pUT/+FG2L6DovCVwC+vMps6OROoDwLWIVTXaMMK0prqpJtm+NzLXaN+qvch9+juaEeHfcvsuI+xU4NRylgfd6fb5tsDKT0t2ThRLB8Z01+8GbsHjIX6BEs3nfR/p+4upt3j0KTWJKZVfh88/G/DaofLtHGNnlq3A2dDyFkXZ4/JjIQf5C/ZFzBqzUtFxX6Lx/AMLMDXFazbd6uI2kVmUsyeS4WIxx7vDYH7C2iZxUAH36B6QvfSqtWTzVNVzIKSHZxMpbXrWzp6CkJWuf53XNJ50JZf0bKIkHum3Tq0S5gsWnZQ6Sub7JHRkdU2OWzHar7Ebdwhws8aKBzQ6ENgT981HYLwd95M0yyP64Z55l06GtTBkSaGIZZekvN15hpqiw8g=',
MD5OfBody: 'd822bfa70dba0a6029f3a156479f7bf8',
Body: '{"messageType":"COMPLETE","roomId":"room-1","albumId":"album-1"}'
}]
};
// DUMMY RESPONSE of AWS.SQS.deleteMessage
const dummyResponseDeleteMessage = {
ResponseMetadata: {RequestId: 'c3824af3-cf44-5dc7-bc7f-1999dcda3f3d'}
};
// AWS.SQS Stub for proxyquire
const awsStub = {
SQS: class {
constructor(options) {
}
sendMessage(params) {
return {
promise: async() => {
return dummyResponseSendMessage
}
};
}
receiveMessage(params) {
return {
promise: async() => {
return dummyResponseReceiveMessage
}
};
}
deleteMessage(params) {
return {
promise: async() => {
return dummyResponseDeleteMessage
}
};
}
}
};
const SQS = proxyquire('./../../lib/sqs', {
'aws-sdk': awsStub
});
const sqsClient = new SQS({QueueUrl: 'http://hogehoge.com'});
describe('check SQS Client using proxyquire', () => {
const roomId = 'room-1';
const photo = {
timestamp: '1525680256000',
id: 'photo-1',
album_id: 'album-1',
content: {
type: 'photo-type',
url: 'photo-url'
}
};
it('should call enqueueNewMessage', async() => {
const res = await sqsClient.enqueueNewMessage(roomId, photo);
assert.deepEqual(dummyResponseSendMessage, res);
});
it('should call enqueueDeleteMessage', async() => {
const res = await sqsClient.enqueueDeleteMessage(roomId, photo);
assert.deepEqual(dummyResponseSendMessage, res);
});
it('should call enqueueCompleteMessage', async() => {
const res = await sqsClient.enqueueCompleteMessage(roomId, photo.album_id);
assert.deepEqual(dummyResponseSendMessage, res);
});
it('should call receiveMessages', async() => {
const messageHandler = message => {
assert.include(['2a669cd3-983d-44d0-9761-d60fd3ba8fa5', 'fe1b2e28-9200-48b2-b2ff-5a82be53c359'], message.MessageId);
};
const res = await sqsClient.receiveMessages(messageHandler);
assert.deepEqual(res, [dummyResponseDeleteMessage, dummyResponseDeleteMessage]);
});
}
);