Not able to mock S3 download request using proxyquire - node.js

const awsSdk = require('aws-sdk');
module.exports = {
downloadFile(bucketName, fileName, callback) {
var s3 = new awsSdk.S3();
var params = { Bucket: bucketName, Key: fileName };
var file = require('fs').createWriteStream('abcd.jpg');
var stream = s3.getObject(params).on('error', function (err) {
console.log(' download.on');
callback('error', null);
})
.createReadStream()
stream.pipe(file);
}
}
Above, is the code i need to mock in my Test class using proxyquire. The issue is, im not able to mock methods like getObject,on,createReadStream. Also after mocking i need to emit the error event from test class that will trigger the above on method which will call the callback in my test class where exception is tested.
Below is my test class code.
const proxyquire = require('proxyquire').noCallThru().noPreserveCache();
const sinon = require('sinon');
const emitter = require('events').EventEmitter;
function Download() {
emitter.call(this);
this.error = function () {
this.emit('error');
}
}
describe('S3 download test', () => {
awsSdkMock = {}
awsSdkMock.config = {};
awsSdkMock.config.region = { };
var s3Instance = {};
awsSdkMock.S3 = function (){
return s3Instance;
};
var object = {};
s3Instance.getObject = function (params){
return object;
};
var request = {};
const errorCallBack = sinon.spy();
var err = 'error';
object.on = function (err, errorCallBack) {
console.log('object.on');
errorCallBack();
return request;
};
var stream = {};
request.createReadStream = function(){
console.log('createReadStream');
return stream
};
stream.pipe = function(file){
console.log('download.error');
// download.error();
};
Download.prototype.__proto__ = emitter.prototype;
var download = new Download();
const s3 = proxyquire('./../../../modules/s3', {
'aws-sdk': awsSdkMock
})
it('Error in download test', (done) => {
const errorCallBack = sinon.spy();
s3.downloadFile('123', 'abcd', errorCallBack);
sinon.assert.calledWith(errorCallBack, 'error', null);
done();
});
})
Any help is appreciated.

Related

Jest return mocked value when S3.getObject function is called

I'm totally new to Jest and typescript. my 2nd test case to be honest.
I want in my jest test - when s3.getObject is called in the actual class, it should return the mocked value.
my handler code:
var aws = require("aws-sdk");
var s3 = new aws.S3({apiVersion: '2006-03-01'});
exports.handler = async function (event, context, callback) {
const bucket = 'event.s3.bucket.name';
const filename = 'fileName';
const inputBucketParams = {
Bucket: bucket,
Key: filename,
};
let result;
try {
**//I want the result to be the mocked value in my test case.**
result = await s3.getObject(inputBucketParams).promise();
const fileContent = getFileContents(result.Body.toString("utf-8"));
my test case:
import {getFileContent} from "../../utils/FileContent";
import anything = jasmine.anything;
const lambdaHandler = require('../lambda/myLambda');
const AWSMock = require('aws-sdk-mock');
const AWS = require("aws-sdk");
describe('Test getS3Object', () => {
beforeEach(() => AWSMock.setSDKInstance(AWS));
})
let s3Object = {
"bucket": {
},
"object": {
}
};
let event = {Records: [
{
s3: s3Object --> from above.
}
]
}
var aws = require("aws-sdk");
var s3 = new aws.S3({apiVersion: '2006-03-01'});
describe('my handler test', async function () {
const s3GetObject = AWSMock.mock('S3', 'getObject', getFileContent('fileName.csv'));
const s3mock = jest.fn();
const getObjectMock = jest.fn(() => getFileContent('fileName.csv'));
const params = {
Bucket: 'bucketName',
Key: 'filename'
}
var returnValue = s3mock.mockReturnValue({
Body: getFileContent('fileName.csv')
});
test('s3 getObject response mock', async () => {
//jest.spyOn(s3, "getObject")
const getObjectMockValue = s3.spyOn('S3', 'getObject').mockReturnValue({
Body: getFileContent('fileName.csv')
})
// my handler is called from this, and when it get to s3.getObject, it fails.
const handlerResponse = await lambdaHandler.handler(event, anything(), anything());
});
});
I want in my jest test - when s3.getObject is called in the actual class, it should return the mocked value.

An issue with reading a gzipped file (.gz) with IBM Cloud Function (Action: Node.js 12)

I can read the data.json.gz file on my local machine with the code mentioned below (node --version: v14.15.0). But when I try to use the same in IBM Cloud with an Action (Node.js 12) to read the same file from an Object Store Bucket, I get the below error
["stderr: ERROR: undefined - input_buf.on is not a function"].
I am very new to NodeJS; Can someone help to identify the issue here?
I do appreciate your support.
Code that works on Local machine (Windows 10):
function decompressFile(filename) {
var fs = require("fs"),
zlib = require("zlib"),
var input = fs.createReadStream(filename);
var data = [];
input.on('data', function(chunk){
data.push(chunk);
}).on('end', function(){
var buf = Buffer.concat(data);
zlib.gunzip(buf, function(err, buffer) {
if (!err) {
var dataString = buffer.toString()
console.log(dataString, dataString+'\n');
var dataJSON = JSON.parse(dataString.toString('utf8'));
}else{
console.log(err);
}
});
});
}
decompressFile("data.json.gz");
Code that does not work on IBM Cloud Function and Object Store Bucket:
// Get file contents of gzipped item
async function getGzippedItem(cosClient, bucketName, itemName) { // <<< async keyword added
const fs = require('fs');
const zlib = require('zlib');
return await cosClient.getObject({ // <<< turned into assignment with await
Bucket: bucketName,
Key: itemName
}).promise()
.then((instream=fs.createReadStream(itemName)) => {
if (instream != null) {
var data = [];
var input_buf = instream.Body
input_buf.on('data', function(chunk){
data.push(chunk);
}).on('end', function() {
var buf = Buffer.concat(data);
zlib.gunzip(buf, function (err, buffer) {
if (!err) {
var dataString = buffer.toString()
var dataJSON = JSON.parse(dataString.toString('utf8'));
} else {
console.log(err);
}
});
});
return buf
}
})
.catch((e) => {
console.error(`ERROR: ${e.code} - ${e.message}\n`);
});
};
async function main(params) {
bucketName = 'bucket'
itemName = 'data.json.gz'
var ibm = require('ibm-cos-sdk');
var util = require('util');
var fs = require('fs');
// Initializing configuration
const myCOS = require('ibm-cos-sdk');
var config = {
endpoint: 'endpoint',
apiKeyId: 'apiKeyId',
ibmAuthEndpoint: 'ibmAuthEndpoint',
serviceInstanceId: 'serviceInstanceId',
};
var cosClient = new myCOS.S3(config);
gzippedItemContent = await getGzippedItem(cosClient, bucketName, itemName) // <<< await keyword added
console.log(">>>>>>>>>>>>>>>: ", typeof gzippedItemContent, gzippedItemContent )
}
The message is telling you, that your input_buf object is not of the type you expect it to be. The result of your createReadStream() call is just a stream:
[Stream] the readable stream object that can be piped or read from (by registering 'data' event listeners).
So you should be able to access the value directly
(not declaring var input_buf = instream.Body):
var getObjectStream = cosClient.getObject({
Bucket: 'BUCKET',
Key: 'KEY'
}).createReadStream();
getObjectStream.on('data', function(c) {
data += c.toString();
});
Have a look at the test section of the ibm-cos-sdk-js project, it is describing how to use the API.

context.hasRole is not a function in Nodejs sinon testing issue

I had made function to fetch cloudwatch details from AWS.I trying to create a testcase in node.js and using sinon but i am getting a context.hasRole is not defined because i am checking this in my function file which is cloudwatch.js.
Can you please help me to fake this test
"-----cloudwatch.spec.js-------"
describe('cloudwatch', () => {
let sandbox = null;
beforeEach(() => {
sandbox = sinon.createSandbox(AWS.config);
})
afterEach(() => {
sandbox.restore()
})
it('Should return queryid', async () => {
let queryId = {
queryId: "12ab3456-12ab-123a-789e-1234567890ab"
};
const body = {
endTime: 34568765,
queryString: 'filter #message like /Audit/',
startTime: 34565678,
limit: 100,
logGroupName: '/aws/lambda/dev-api--service-sandbox-api'
};
let params = {}
let queryid = {
queryId: 6786971301298309123
};
await cloudwatch.startQuery(context, params, body, callback);
sinon.match(queryId)
})
})
"------Cloudwatch.js----"
let cloudwatch = module.exports = {};
const AWS = require('aws-sdk');
const nconf = require('nconf');
const {
HttpResult,
HttpUnauthorizedError,
AmazonCloudWatchLogsClient
} = require('api-lib');
AWS.config = nconf.get('amazonCloudWatchLogsClient');
cloudwatch.startQuery = async function(context, params, body,
callback) {
body.startTime = new Date(body.startTime).valueOf();
body.endTime = new Date(body.endTime).valueOf();
if (!context.hasRole("read:cloudwatch"))
return callback(new HttpUnauthorizedError("context missing role
read:cloudwatch"));
const amazonCloudWatchLogsClient = new
AmazonCloudWatchLogsClient(AWS.config);
let result = await amazonCloudWatchLogsClient.startQuery(body,
function(err) {
console.log("Error", err);
});
callback(null, new HttpResult(result));
};
cloudwatch.getQueryResults = async function(context, params,
requestBody, callback) {
console.log(requestBody)
let test = requestBody.queryId;
test = test.toString();
requestBody.queryId = test;
if (!context.hasRole("read:cloudwatch"))
return callback(new HttpUnauthorizedError("context missing role
read:cloudwatch"));
const amazonCloudWatchLogsClient = new
AmazonCloudWatchLogsClient(AWS.config);
let result2 = await
amazonCloudWatchLogsClient.getQueryResults(requestBody.queryId,
function(err) {
console.log("Error", err);
});
callback(null, new HttpResult(result2));
};
I am using eslint and except from chai for comparing the output to the sample output.

Retrieve data from S3 bucket with Lambda function link to an alexa skill in node.js

What i'm trying to do here is to retrieve a basic CSV file in a S3 Bucket but i'm having hard time getting it.
I try first to only get the data from the bucket in a basic lambda function (not linked to an alexa skill) like that :
console.log('Loading function');
const aws = require('aws-sdk');
const s3 = new aws.S3({ apiVersion: '2006-03-01' });
exports.handler = async (event, context) => {
const bucket = 'mybucket';
const key = 'myfile';
const params = {
Bucket: bucket,
Key: key,
};
try {
const data = await s3.getObject(params).promise();
var content = data.Body.toString();
var lines = content.split('\r\n');
var headers = lines[0].split(',');
var result = {};
for(var i=1;i<lines.length;i++){
var currentline = lines[i].split(',');
var obj2 = {};
for(var j=2;j<headers.length;j++){
obj2[headers[j]] = currentline[j];
}
result[currentline[0]+currentline[1]] = obj2;
}
return result;
} catch (err) {
console.log(err);
const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`;
console.log(message);
throw new Error(message);
}
};
And it worked perfectly but then i tried to put it inside a lambda function linked to my alexa skill and i'm not receiving anything from the getObject function :
const Alexa = require('alexa-sdk');
const aws = require('aws-sdk');
const s3 = new aws.S3({ apiVersion: '2006-03-01' });
var data;
function getData(){
// Setting the bucket and files parameters
const bucket = 'mybucket';
const key = 'myfile';
const params = {
Bucket: bucket,
Key: key,
};
try {
const data = s3.getObject(params);
// Getting the Body of the response
var content = data.Body.toString();
// Splitting into a proper data structure
var result = {};
var lines = content.split("\r\n");
var headers = lines[0].replace('\r','').split(",");
for(var i=1;i<lines.length;i++){
var currentline = lines[i].split(",");
var obj2 = {};
for(var j=2;j<headers.length;j++){
obj2[headers[j]] = currentline[j];
}
result[currentline[0].toLowerCase()+currentline[1].toLowerCase()] = obj2;
}
} catch (err) {
console.log(err);
const message = `Error getting object ${key} from bucket ${bucket}.`;
console.log(message);
throw new Error(message);
}
// Sending the response back
return result;
}
const handlers = {
'LaunchRequest': function () {
// GETTING THE DATA SET
data = getData();
// Stuff here
this.emit(':responseReady');
},
'getLocalisation': function () {
//Stuff here
},
'AMAZON.HelpIntent': function () {
this.response.speak(messages.HELP);
this.emit(':responseReady');
},
'AMAZON.CancelIntent': function () {
this.response.speak(messages.STOP);
this.emit(':responseReady');
},
'AMAZON.StopIntent': function () {
this.emit(':tell', 'Bye');
},
'AMAZON.FallbackIntent': function () {
this.response.speak(messages.ERROR);
this.emit(':responseReady');
},
};
exports.handler = (event, context, callback) => {
const alexa = Alexa.handler(event, context, callback);
alexa.APP_ID = APP_ID;
alexa.registerHandlers(handlers);
alexa.execute();
};
I'm running everything directly from the AWS Lambda IDE.
I think i'm missing something with Async/Await/Promise thing but i don't truely understand it.
I just had to put
async function getData()
and
exports.handler = async (event, context, callback) => {
// code here
};

Unable to access dynamodb from nodejs code through lambda

I have been trying to test this from lambda but unable to proceed.
var Alexa = require("alexa-sdk");
var dynamoDBConfiguration = {
"accessKeyId": "useraccess",
"secretAccessKey": "usersecretkey",
"region": "us-east-1"
};
var AWS = require("aws-sdk");
AWS.config.update(dynamoDBConfiguration);
var promisify = require("es6-promisify");
var dynClient = new AWS.DynamoDB.DocumentClient({"region":"us-east-
1"});
//convert callback style functions to promises
const dbGet = promisify(dynClient.get,dynClient);
const dbPut = promisify(dynClient.put,dynClient);
const dbDelete = promisify(dynClient.delete,dynClient);
var startStateHandlers = (GAME_STATES.STARt, {
"StartGame": function (isNewGame,context) {
const dynamoParams = {
TableName: "Userdata",
Key: {
"UserId":"test"
}
}
dbGet(dynamoParams)
.then(data => {
console.log('Get user succeeded', data);
const userId = data.UserId;
if (userId != null) {
console.log(data.UserName);
}
else {
// no match, add the user
return dbPut(dynamoParams);
}
})
.then(data => {
console.log('Add user succeeded', data);
})
.catch(err => {
console.error(err);
});
this.emit(":tell", speechOutput, speechOutput);
}
});
var handlers = {
"LaunchRequest": function () {
var speechOutput = "hello";
this.emit(":tell", speechOutput, speechOutput);
}
};
var handler = (function () {
function handler(event, context, callback) {
var alexa = Alexa.handler(event, context);
alexa.appId = "appid";
alexa.registerHandlers(handlers, startStateHandlers);
alexa.execute();
}
return handler;
})();
exports.handler = handler;
It doesnt throw any error in the lamda logs and not able to see any log in Cloudwatch. It just executes the other lines of code.
I have attached all permission policy to the user and setup the lamda function as lambda_dynamo.
Unable to understand what is the issue with it.
Appreciate your help.
var dynamoDBConfiguration = {
"accessKeyId": "useraccess",
"secretAccessKey": "usersecretkey",
"region": "us-east-1"
};
var AWS = require("aws-sdk");
// update the AWS.Config global configuration object
AWS.config.update(dynamoDBConfiguration)
// Create DynamoDB document client
var dynClient = new AWS.DynamoDB.DocumentClient()

Resources