aws-sdk-mock icon indicating copy to clipboard operation
aws-sdk-mock copied to clipboard

getSignedUrl doesn't mock correctly when called asynchronously

Open cfurst opened this issue 5 years ago • 16 comments

code:

AWSMock.mock('S3', 'getSignedUrl', 'message');
const s3 = new AWS.S3({ paramValidation: true }),
const s3Url = s3.getSignedUrl('getObject', {Key: 'key', Bucket: 'Bucket'});
console.log(s3Url); //should be a string... However it looks more like an AWS Request object:

Output:

{ promise: [Function],
  createReadStream: [Function: createReadStream],
  on: [Function: on],
  send: [Function: send] }

Please review the documentation for further information: https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#getSignedUrl-property

cfurst avatar Oct 04 '19 22:10 cfurst

It seems this has something to do with the assumption that all the AWS service methods should have the signature service(params, callback)

getSignalUrl has the following signature: getSignalUrl(method,params, callback)

I'd really appreciate it if this could be fixed

krizzje avatar Jan 09 '20 13:01 krizzje

I was able to get this working by NOT using the getSignedUrlPromise:

  AWSMock.setSDKInstance(AWS);
    AWSMock.mock('S3','getSignedUrl', (operation,params,callback) => {
      return callback(null, fixtureObject);
    });

Code is:

   const s3 = new AWS.S3(s3ConfigObject);
    return new Promise((resolve, reject) => {
        s3.getSignedUrl('putObject', params, (err, url) => {
            err ? reject(err) : resolve(url);
        });
    });

yg-dba avatar Feb 05 '20 22:02 yg-dba

Any update on when this is going to be fixed?

GoogleMikeNY avatar Feb 13 '20 22:02 GoogleMikeNY

How can I mock to getSignedUrl ?

code:

public async getUrlFromS3(fileName: string): Promise<string> {
    const params = {
      Bucket: this.bucketName,
      Key: `${fileName}.pdf`,
      Expires: config.pdfUrlExpiredTime, 
    };
    return new Promise((resolve, reject): any => {
      this.s3.getSignedUrl("getObject", params, (err: Error, url: string) => {
        console.log("call end");
        if (err) {
          reject(new HttpErrorWrapper(err));
        }
        resolve(url);
      });
    });
  }

and my test:

AWSMock.mock("S3", "getSignedUrl", (operation, params, callback) => {
        console.log("mock called");
        callback(null, "https://example.com");
      });

but it was not log and return url is not that, but https://s3.amazonaws.com/~~~~~

WonJaesub avatar Mar 02 '20 02:03 WonJaesub

So I ran into this issue also, I was able to get around it for now with mocking the getSignedUrlPromise function manually:

test('will return a 200', async () => {
    const listObjectsV2Mock = jest.fn().mockReturnValue({
      promise: jest.fn().mockResolvedValue({
        Contents: [
          {
            Key: 'testing/',
            LastModified: '2020-03-14T01:24:24.000Z',
            ETag: '"d41d8cd98f00b204e9800998ecf8427e"',
            Size: 0,
            StorageClass: 'STANDARD'
          }
        ]
      })
    });

    const putObjectMock = jest.fn().mockReturnValue({
      promise: jest.fn().mockResolvedValue({})
    });

    const getSignedUrlPromise = jest
      .fn()
      .mockResolvedValue('Successfully Mocked URL');

    AWSMock.mock('S3', 'getSignedUrlPromise', (operation, params, callback) => {
      return callback(null, 'test');
    });

    AWS.S3 = jest.fn().mockImplementation(() => ({
      getSignedUrlPromise,
      listObjectsV2: listObjectsV2Mock,
      putObject: putObjectMock
    }));

    const { exportDataToCsv } = require('./exportDataToCsv');
    const result = await exportDataToCsv({}, {});
    expect(result).toBe({ statusCode: 200 });
  }); 

This allowed me to mock the getSignedUrlPromise function whilst also mocking others. The issue I had with the others was when I injected the manually mocked functions whilst using aws-sdk-mock I was losing all reference to the stubs previously in place, so I have simply not used this repo for this functions unit tests. Not a great solution but might be helpful for others that need a solution now.

mattbryce93 avatar Mar 16 '20 19:03 mattbryce93

I created a palliative for this case

const aws = require('aws-sdk');
const sinon = require('sinon');


class AwsMock {
  getSignedUrl() {
    return 'signedUrl.com';
  }
  async getSignedUrlPromise() {
    return 'signedUrlAsync.com';
  }
}
const awsS3Mock = sinon.stub(aws, 'S3').returns(new AwsMock());

const s3 = new aws.S3();

const urlAsync = await s3.getSignedUrlPromise('s3.link');
console.log(urlAsync); // signedUrlAsync.com

const url = s3.getSignedUrl('s3.link');
console.log(url); // signedUrl.com

fellipe-pinheiro avatar Apr 23 '20 18:04 fellipe-pinheiro

I had the same issue with this code:

const url = s3.getSignedUrl('putObject', s3Params)

and my test:

AWS.mock('S3', 'getSignedUrl', (action, _params, callback) => {
  console.log('S3', 'getSignedUrl', 'mock called')
  callback(null, mockSignedUrl)
})

so, I changed my code to this, and then my test worked!

const url = await s3.getSignedUrlPromise('putObject', s3Params)

yoelfme avatar Jun 12 '20 19:06 yoelfme

we should not change implementation of our code to pass tests but rather the aws-sdk-mock should support mocking of all features of aws-sdk

ivanmartos avatar Nov 24 '20 09:11 ivanmartos

Also experiencing this issue.

sraleci avatar Mar 29 '21 21:03 sraleci

It seems that this library only mocks methods that return instances of the Request<D, E> class. However, there are lots of methods that don't return such instances, for example, the aforementioned getSignedUrl. Are there future plans to add the possibility to mock such methods?

serguntchik avatar Aug 09 '21 06:08 serguntchik

Just ran over this issue on a project and @yoelfme 's approach was very helpful for my case, with the difference that i already was using the s3.getSignedUrlPromise method, so i just had to create a mock for s3.getSignedUrl and that was enough for the test to work without problems.

biomorgoth avatar Oct 28 '21 21:10 biomorgoth

Had the same issue.

I too solved it by mocking getSignedUrl instead of getSignedUrlPromise.

xarvh avatar Apr 08 '22 05:04 xarvh

Yes, looks like getSignedUrlPromise method was used in your code you still have to mock getSignedUrl method: AWSMock.mock('S3', 'getSignedUrl', signedUrl);

VladislavLobakh avatar Jul 28 '22 14:07 VladislavLobakh

Same problem here. I don't want to modify my client to use the promise version. How can I get this working in Jest

davec-fvr avatar Apr 04 '23 09:04 davec-fvr

How to mock getSignedUrl from SDK v3 using Jest?

import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
...
export const handler = async (event) => {
  ...
  const putCommand = new PutObjectCommand(myParams);
  await getSignedUrl(new S3Client(), putCommand);
}

code-by avatar Jun 18 '23 21:06 code-by

Just ran over this issue on a project and @yoelfme 's approach was very helpful for my case, with the difference that i already was using the s3.getSignedUrlPromise method, so i just had to create a mock for s3.getSignedUrl and that was enough for the test to work without problems.

What SDK you use - v2 or v3 ?

code-by avatar Jun 19 '23 12:06 code-by