Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking Secrets Manager module for JavaScript jest unit tests

I'm having trouble getting the AWS Secrets Manager module mocked for the jest unit tests... The part it errors on is the .promise(). When I remove that, the code doesn't work for the real Secrets Manager so I think it needs to stay there. How do I mock the getSecretData function so that getSecretData.promise() will work for the mock?

Here is the SecretsManager.js code:

import AWS from 'aws-sdk';

export class SecretsManager {
  constructor() {
    AWS.config.update({
      region: 'us-east-1',
    });
    this.secretsManager = new AWS.SecretsManager();
  }

  async getSecretData(secretName) {
    try {
      const response = await this.secretsManager.getSecretValue({
        SecretId: secretName,
      }).promise();
      const secretString = response.SecretString;
      const parsedSecret = JSON.parse(secretString);
      return parsedSecret;
    } catch (e) {
      console.log('Failed to get data from AWS Secrets Manager.');
      console.log(e);
      throw new Error('Unable to retrieve data.');
    }
  }
}

Here is the SecretsManager.test.js code:

import { SecretsManager } from '../utils/SecretsManager';

jest.mock('aws-sdk', () => {
  return {
    config: {
      update(val) {

      },
    },
    SecretsManager: function () {
      return {
        async getSecretValue({
          SecretId: secretName
        }) {
          return {
            promise: function () {
              return {
                 UserName: 'test',
                 Password: 'password',
              };
            }
          };
        }
      };
    }
  }

});


describe('SecretsManager.js', () => {
  describe('Given I have a valid secret name', () => {
    describe('When I send a request for test_creds', () => {
      it('Then the correct data is returned.', async () => {
        const mockReturnValue = {
          UserName: 'test',
          Password: 'password',
        };
        const logger = getLogger();
        const secretManager = new SecretsManager();
        const result = await secretManager.getSecretData('test_creds');
        expect(result).toEqual(mockReturnValue)
      });
    });
    describe('When I send a request without data', () => {
      it('Then an error is thrown.', async () => {
      const secretManager = new SecretsManager();
      await expect(secretManager.getSecretData()).rejects.toThrow();
      });
    });
  });
});

This is the error I get when running the tests:

 this.secretsManager.getSecretValue(...).promise is not a function

Any suggestions or pointers are greatly appreciated!
Thank you for looking at my post.

like image 247
dotteddice Avatar asked Jan 24 '23 23:01

dotteddice


1 Answers

I finally got it to work... figures it'd happen shortly after posting the question, but instead of deleting the post I'll share how I changed the mock to make it work incase it helps anyone else.

Note: This is just the updated mock, the tests are the same as in the question above.

// I added this because it's closer to how AWS returns data for real.
const mockSecretData = {
  ARN: 'x',
  Name: 'test_creds',
  VersionId: 'x',
  SecretString: '{"UserName":"test","Password":"password"}',
  VersionStages: ['x'],
  CreatedDate: 'x'
}

jest.mock('aws-sdk', () => {
  return {
    config: {
      update(val) {
      },
    },
    SecretsManager: function () {
      return {
        getSecretValue: function ( { SecretId } ) {
          {
           // Adding function above to getSecretValue: is what made the original ".promise() is not a function" error go away.

            if (SecretId === 'test_creds') {
              return {
                promise: function () {
                  return mockSecretData;
                }
              };
            } else {
              throw new Error('mock error');
            }
        }
      }
    };
  }
}});
like image 114
dotteddice Avatar answered Jan 27 '23 13:01

dotteddice