Giter Club home page Giter Club logo

aws-sdk-client-mock's Introduction

aws-sdk-client-mock's People

Contributors

alexforsyth avatar dependabot[bot] avatar geshan avatar github-actions[bot] avatar jurijzahn8019 avatar justsml avatar lobbin avatar m-radzikowski avatar marcusleg avatar pgeske avatar richardbradley avatar ryansonshine avatar samchungy avatar sander-b-postnl avatar swierzbicki avatar yaronya avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

aws-sdk-client-mock's Issues

Support passing a callback to `.resolves()`

Currently, you can only pass a static object to .resolves(). This is okay if your requests contain different input you can match on, but if you want to pass different results for two calls that use the same input, I don't see a way to do this currently.

Allowing a function to be passed instead of an object would allow tests to do things such as returning an error for the first request, but returning a success response when that request happens a second time.

I would think this could be a quick addition, but I'm quite bad with typescript, so I'm hoping someone else can submit a PR for this

Client creation test

When mocking an aws client I would like to be able to also test the configuration given to the client during initialization.

Assume I have a util function:

export const myFunction = async (config: S3ClientConfig) => {
    const client = new S3Client(config)
    await client.send( ... )
}

And in my test I have:

describe('myFunction', () => {
    it('should create a s3 client with given configuration and send ...', () => {
        const s3mock = mockClient(S3Client)
        const myConfig = { ... }

        await expect(myFunction(myConfig)).resolves.toBeUndefined()
        expect(s3mock.calls()).toHaveLength(1)

        // here I would also like to test if the config given to the s3 client is exactly the one 
        // I gave to myFunction call as parameter
    })
})

Is there already a way to achieve this?

Move `tslib` to dependencies from devDependencies

This module does rely on tslib at runtime, so strict node systems like yarn v2 and esbuild will not allow it to run without a patch.

A temporary workaround for yarn v2 projects is to put:

packageExtensions:
  aws-sdk-client-mock@*:
    dependencies:
      tslib: '*

In your .yarnrc.yml file in order to patch this library, but the better fix will be to update this package to correctly put tslib as a dependency.

See similar issues: styled-components/styled-components#3082

Issues with mocking a DynamoDBDocumentClient with PutCommand

Hey!
I'm currently using DynamoDBDocumentClient in order to update an item, like this:

//index.ts
 const updateItem = async function (item: Item, tableName: string) {
  const response = await ddbDocumentClient.send(
    new PutCommand({
      TableName: tableName,
      Item: {
        ...item,
      },
      ReturnValues: "ALL_OLD",
    })
  )
  //for debugging purposes
  console.log('response', response)
  return response
}

At first, I wanted to mock a rejection (in case the code cannot reach the DynamoDB table) like this:

//index.test.ts
let ddbMock
  let badEvent
  beforeEach(() => {
    uuid.mockImplementation(() => "testid")
    ddbMock = mockClient(DynamoDBDocumentClient)
    ddbMock.reset()
  })
...
ddbMock
      .on(PutCommand, {
        TableName: process.env.TECHNICAL_ASSET_TABLE_NAME,
        Item: { ...happyTA },
        ReturnValues: "ALL_OLD",
      }).rejects('Random error')

And the console returned me the following : response undefined
So, the 2nd time I tried returning something to make sure the resolve() worked:

//index.test.ts
let ddbMock
  let badEvent
  beforeEach(() => {
    uuid.mockImplementation(() => "testid")
    ddbMock = mockClient(DynamoDBDocumentClient)
    ddbMock.reset()
  })
...
ddbMock
      .on(PutCommand, {
        TableName: process.env.TECHNICAL_ASSET_TABLE_NAME,
        Item: { ...happyTA },
        ReturnValues: "ALL_OLD",
    // I got the following resolves data from a console log I did a while back... But I feel like the PutCommand isn't resolving anything actually.
      }).resolves({
        $metadata: {
          httpStatusCode: 200,
          requestId: "QF505AUD62FFNV43SFJJLL9NR7VV4KQNSO5AEMVJF66Q9ASUAAJG",
          extendedRequestId: undefined,
          cfId: undefined,
          attempts: 1,
          totalRetryDelay: 0,
        },
        Attributes: undefined,
        ConsumedCapacity: undefined,
        ItemCollectionMetrics: undefined,
    })

And same thing, the response kept being disregarded... Am I resolving/rejecting the wrong way?

Thanks in advance for your insight!

EventBridge client not supported.

When I tried to use this library, as documented, with the EventBridge client. I got this:

const mockEventBridgeClient = mockClient(EventBridgeClient);

Argument of type 'typeof EventBridgeClient' is not assignable to parameter of type 'InstanceOrClassType<Client<ServiceInputTypes, MetadataBearer, any>>'.
      Type 'typeof EventBridgeClient' is not assignable to type 'ClassType<Client<ServiceInputTypes, MetadataBearer, any>>'.
        The types of 'prototype.middlewareStack.concat' are incompatible between these types.
          Type '<InputType extends ServiceInputTypes, OutputType extends ServiceOutputTypes>(from: MiddlewareStack<InputType, OutputType>) => MiddlewareStack<InputType, OutputType>' is not assignable to type '<InputType extends ServiceInputTypes, OutputType extends MetadataBearer>(from: MiddlewareStack<InputType, OutputType>) => MiddlewareStack<InputType, OutputType>'.
            Types of parameters 'from' and 'from' are incompatible.
              Type 'MiddlewareStack<InputType, OutputType>' is not assignable to type 'MiddlewareStack<InputType, ServiceOutputTypes>'.
                Types of property 'addRelativeTo' are incompatible.
                  Type '(middleware: MiddlewareType<InputType, OutputType>, options: RelativeMiddlewareOptions) => void' is not assignable to type '(middleware: MiddlewareType<InputType, ServiceOutputTypes>, options: RelativeMiddlewareOptions) => void'.
                    Types of parameters 'middleware' and 'middleware' are incompatible.
                      Type 'MiddlewareType<InputType, ServiceOutputTypes>' is not assignable to type 'MiddlewareType<InputType, OutputType>'.
                        Type 'InitializeMiddleware<InputType, ServiceOutputTypes>' is not assignable to type 'MiddlewareType<InputType, OutputType>'.
                          Type 'InitializeMiddleware<InputType, ServiceOutputTypes>' is not assignable to type 'InitializeMiddleware<InputType, OutputType>'.
                            Call signature return types 'InitializeHandler<InputType, ServiceOutputTypes>' and 'InitializeHandler<InputType, OutputType>' are incompatible.
                              Type 'Promise<InitializeHandlerOutput<ServiceOutputTypes>>' is not assignable to type 'Promise<InitializeHandlerOutput<OutputType>>'.
                                Type 'InitializeHandlerOutput<ServiceOutputTypes>' is not assignable to type 'InitializeHandlerOutput<OutputType>'.
                                  Types of property 'output' are incompatible.
                                    Type 'ServiceOutputTypes' is not assignable to type 'OutputType'.
                                      'ServiceOutputTypes' is assignable to the constraint of type 'OutputType', but 'OutputType' could be instantiated with a different subtype of constraint 'MetadataBearer'.
                                        Type 'ActivateEventSourceCommandOutput' is not assignable to type 'OutputType'.
                                          'ActivateEventSourceCommandOutput' is assignable to the constraint of type 'OutputType', but 'OutputType' could be instantiated with a different subtype of constraint 'MetadataBearer'.

Cannot find module '@aws-sdk/client-s3'

Checklist

  • I have read Caveats documentation and didn't find a solution for this problem there.

Bug description
On version 0.5.3 my tests were running successfully, but after the latest change I'm getting a module error for '@aws-sdk/client-s3'. This is issue started for me on 0.5.4 - I reverted back to the previous version and it's working again as expected.

I don't use @aws-sdk/client-s3 at all in my application/test code.

Error: Cannot find module '@aws-sdk/client-s3'
Require stack:
- /home/circleci/libs/node_modules/aws-sdk-client-mock/dist/cjs/libStorage.js
- /home/circleci/libs/node_modules/aws-sdk-client-mock/dist/cjs/index.js
- /home/circleci/libs/packages/lib-param-store/test/index_test.js
- /home/circleci/libs/node_modules/mocha/lib/esm-utils.js
- /home/circleci/libs/node_modules/mocha/lib/mocha.js
- /home/circleci/libs/node_modules/mocha/lib/cli/one-and-dones.js
- /home/circleci/libs/node_modules/mocha/lib/cli/options.js
- /home/circleci/libs/node_modules/mocha/lib/cli/cli.js
- /home/circleci/libs/node_modules/mocha/lib/cli/index.js
- /home/circleci/libs/node_modules/mocha/bin/_mocha

Environment

  • Node version: Node 14.X
  • Typescript version: Javascript
  • AWS SDK v3 Client mock version: 0.5.4
  • AWS JS SDK libs and versions:
    • "@aws-sdk/client-ssm": "3.26.0"

Support for S3 Upload from @aws-sdk/lib-storage

@aws-sdk/lib-storage provides an Upload capability for easy and efficient uploading of buffers, blobs, or streams, using a configurable amount of concurrency to perform multipart uploads where possible. This abstraction enables uploading large files or streams of unknown size due to the use of multipart uploads under the hood.

It is unclear if it is possible to mock this using this library, if it is please can you add to the documentation in a similar way to the dynamo document client. If it is not please can you consider adding this feature.

Kind regards

Cannot find name 'queueMicrotask'

aws-sdk-client-mock v0.5.0
jest v27.0.4
TypeScript v9.1.1

const iotClientMock = mockClient(IoTClient);
  iotClientMock.on(DescribeJobCommand, {
    jobId: expected.job.jobId,
  }).resolves({
    job: expected.job,
  });
๐Ÿค– test ยป test:compile | tsc --noEmit --project tsconfig.jest.json
node_modules/@sinonjs/fake-timers/types/fake-timers-src.d.ts:11:28 - error TS2304: Cannot find name 'queueMicrotask'.

11     queueMicrotask: typeof queueMicrotask;

sinon v11 broke sinon.match assertions on your mock

your package depends on sinon v9, that depends on @sinonjs/samsam v5 (the package used to implement matchers)

sinon released v11 that depends on @sinonjs/samsam v6.
installing sinon v11 in my project together with aws-sdk-client-mock results in duplicates of both packages installed, thus breaking tests with assertions like this:

sinon.assert.calledWithExactly(s3mock.send, sinon.match.any)

you should either:

  • upgrade to sinon v11
  • move sinon to peer dependencies to not duplicate it
  • expose sinon.match* through your package

An easy way to test params sent to command

Hi

First thanks so much for creating this :)!

Second, I was wondering if a function could be made to easily retrieve the params sent to the command

e.g.

    EndpointArn: endpointArn ?? '',
    Attributes: {
      Enabled: 'true',
      Token: token,
      CustomUserData: JSON.stringify(userData),
    },
  }
  const updatePlatformEndpoint = snsClient.send(
    new SetEndpointAttributesCommand(params),
  )

Then you could have the mock do something like:

params = snsMock.on(SetEndpointAttributesCommand).params()

So it's easy to test the params being sent through?

Type errors for AWS SDK 3.10.0

I'm having some trouble with types. Using //@ts-ignore works but probably isn't solving the problem.

Example code:

import {mockClient} from 'aws-sdk-client-mock';
import {GetQueueAttributesCommand, GetQueueUrlCommand, SQSClient} from '@aws-sdk/client-sqs';
import {AssertQueueLength} from '../../../src/sqs/SQSChecks';

const sqsMock = mockClient(SQSClient);

beforeEach(() => {
    sqsMock.reset();
});

describe('AssertQueueLength', () => {
    it('Should return true when the approx. queue length matches the expected value.', async () => {
        sqsMock.on(GetQueueUrlCommand).resolves({
            QueueUrl: 'http://aws.com/queue'
        });

        sqsMock.on(GetQueueAttributesCommand).resolves({
            Attributes: {
                ApproximateNumberOfMessages: '1'
            }
        });

        expect(await AssertQueueLength('my-queue', 1)).toBeTruthy();
    });
});

Error thrown:

 FAIL  packages/checks/tests/units/sqs/SQSHelper.spec.ts
  โ— Test suite failed to run

    packages/checks/tests/units/sqs/SQSHelper.spec.ts:6:28 - error TS2345: Argument of type 'typeof SQSClient' is not assignable to parameter of type 'InstanceOrClassType<Client<ServiceInputTypes, ServiceOutputTypes, any>>'.
      Type 'typeof SQSClient' is not assignable to type 'ClassType<Client<ServiceInputTypes, ServiceOutputTypes, any>>'.
        The types of 'prototype.middlewareStack.add' are incompatible between these types.
          Type '{ (middleware: import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/smithy-client/node_modules/@aws-sdk/types/dist/types/middleware").InitializeMiddleware<import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/client-sqs/dist/types/SQSClient").ServiceInputTypes, import("/home/alex/dev/man...' is not assignable to type '{ (middleware: import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/types/types/ts3.4/middleware").InitializeMiddleware<import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/client-sqs/dist/types/SQSClient").ServiceInputTypes, import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/cl...'.
            Types of parameters 'middleware' and 'middleware' are incompatible.
              Types of parameters 'context' and 'context' are incompatible.
                Type 'import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/smithy-client/node_modules/@aws-sdk/types/dist/types/middleware").HandlerExecutionContext' is not assignable to type 'import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/types/types/ts3.4/middleware").HandlerExecutionContext'.
                  Types of property 'userAgent' are incompatible.
                    Type 'import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/smithy-client/node_modules/@aws-sdk/types/dist/types/util").UserAgent' is not assignable to type 'import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/types/types/ts3.4/util").UserAgent'.
                      Type 'import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/smithy-client/node_modules/@aws-sdk/types/dist/types/util").UserAgentPair' is not assignable to type 'import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/types/types/ts3.4/util").UserAgentPair'.
                        Types of property 'length' are incompatible.
                          Type '4' is not assignable to type '2'.

    6 const sqsMock = mockClient(SQSClient);
                                 ~~~~~~~~~
    packages/checks/tests/units/sqs/SQSHelper.spec.ts:15:20 - error TS2345: Argument of type 'typeof GetQueueUrlCommand' is not assignable to parameter of type 'new (input: GetQueueUrlRequest) => AwsCommand<GetQueueUrlRequest, GetQueueUrlCommandOutput, any, any>'.
      Construct signature return types 'GetQueueUrlCommand' and 'AwsCommand<GetQueueUrlRequest, GetQueueUrlCommandOutput, any, any>' are incompatible.
        The types of 'middlewareStack.add' are incompatible between these types.
          Type '{ (middleware: import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/smithy-client/node_modules/@aws-sdk/types/dist/types/middleware").InitializeMiddleware<import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/client-sqs/dist/types/models/models_0").GetQueueUrlRequest, import("/home/alex/dev/me...' is not assignable to type '{ (middleware: import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/types/types/ts3.4/middleware").InitializeMiddleware<import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/client-sqs/dist/types/models/models_0").GetQueueUrlRequest, import("/home/alex/dev/manage-alerts/node_modules/@aws...'.
            Types of parameters 'middleware' and 'middleware' are incompatible.
              Types of parameters 'context' and 'context' are incompatible.
                Type 'import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/smithy-client/node_modules/@aws-sdk/types/dist/types/middleware").HandlerExecutionContext' is not assignable to type 'import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/types/types/ts3.4/middleware").HandlerExecutionContext'.

    15         sqsMock.on(GetQueueUrlCommand).resolves({
                          ~~~~~~~~~~~~~~~~~~
    packages/checks/tests/units/sqs/SQSHelper.spec.ts:20:20 - error TS2345: Argument of type 'typeof GetQueueAttributesCommand' is not assignable to parameter of type 'new (input: GetQueueAttributesRequest) => AwsCommand<GetQueueAttributesRequest, GetQueueAttributesCommandOutput, any, any>'.
      Construct signature return types 'GetQueueAttributesCommand' and 'AwsCommand<GetQueueAttributesRequest, GetQueueAttributesCommandOutput, any, any>' are incompatible.
        The types of 'middlewareStack.add' are incompatible between these types.
          Type '{ (middleware: import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/smithy-client/node_modules/@aws-sdk/types/dist/types/middleware").InitializeMiddleware<import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/client-sqs/dist/types/models/models_0").GetQueueAttributesRequest, import("/home/alex...' is not assignable to type '{ (middleware: import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/types/types/ts3.4/middleware").InitializeMiddleware<import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/client-sqs/dist/types/models/models_0").GetQueueAttributesRequest, import("/home/alex/dev/manage-alerts/node_modul...'.
            Types of parameters 'middleware' and 'middleware' are incompatible.
              Types of parameters 'context' and 'context' are incompatible.
                Type 'import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/smithy-client/node_modules/@aws-sdk/types/dist/types/middleware").HandlerExecutionContext' is not assignable to type 'import("/home/alex/dev/manage-alerts/node_modules/@aws-sdk/types/types/ts3.4/middleware").HandlerExecutionContext'.

    20         sqsMock.on(GetQueueAttributesCommand).resolves({
                          ~~~~~~~~~~~~~~~~~~~~~~~~~

If I add // @ts-ignore as below then all works as expected:

import {mockClient} from 'aws-sdk-client-mock';
import {GetQueueAttributesCommand, GetQueueAttributesResult, GetQueueUrlCommand, SQSClient} from '@aws-sdk/client-sqs';
import {AssertQueueLength} from '../../../src/sqs/SQSChecks';

// @ts-ignore
const sqsMock = mockClient(SQSClient);

beforeEach(() => {
    sqsMock.reset();
});

describe('AssertQueueLength', () => {
    it('Should return true when the approx. queue length matches the expected value.', async () => {
        // @ts-ignore
        sqsMock.on(GetQueueUrlCommand).resolves({
            QueueUrl: 'http://aws.com/queue'
        });

        // @ts-ignore
        sqsMock.on(GetQueueAttributesCommand).resolves({
            Attributes: {
                ApproximateNumberOfMessages: '1'
            }
        });

        expect(await AssertQueueLength('my-queue', 1)).toBeTruthy();
    });
});

Packages:

  "dependencies": {
    "@aws-sdk/client-sqs": "^3.10.0"
  },
  "devDependencies": {
    "aws-sdk-client-mock": "^0.2.0"
  }

ts-config:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["es2020"]
  },
  "include": [
    "packages/*"
  ]
}

Any help would be appreciated.

v0.6.0 does not provide an export named 'mockClient'

Checklist

  • [x ] I have read Caveats documentation and didn't find a solution for this problem there.

Bug description

Since the 0.6.0 version I am getting syntax errors when trying to use the import { mockClient } from 'aws-sdk-client-mock' syntax.

SyntaxError: The requested module 'aws-sdk-client-mock' does not provide an export named 'mockClient'

      at Runtime.linkAndEvaluateModule (node_modules/jest-runtime/build/index.js:779:5)
      at TestScheduler.scheduleTests (node_modules/@jest/core/build/TestScheduler.js:333:13)
      at runJest (node_modules/@jest/core/build/runJest.js:404:19)
      at _run10000 (node_modules/@jest/core/build/cli/index.js:320:7)

Environment

  • Node version: 17.3.0
  • Testing lib and version: Jest 27.5.1
  • Typescript version: /
  • AWS SDK v3 Client mock version: 0.6.0
  • AWS JS SDK libs and versions:
    • @aws-sdk/client-rds-data v3.53.0

Minimal example

Running npm run test with the following minimal sample generates the syntax error under v0.6.0 of the library, but runs fine under v0.5.6

package.json

{
    "name": "aws-sdk-client-mock-poc",
    "version": "0.0.1",
    "main": "index.mjs",
    "type": "module",
    "scripts": {
        "test": "cross-env NODE_OPTIONS='--experimental-vm-modules' jest"
    },
    "devDependencies": {
        "aws-sdk-client-mock": "^0.6.0",
        "cross-env": "^7.0.3",
        "jest": "^27.5.1"
    },
    "jest": {
        "moduleFileExtensions": [
            "mjs",
            "js"
        ],
        "testMatch": [
            "**/*.test.mjs"
        ]
    }
}

index.test.mjs

import { mockClient } from 'aws-sdk-client-mock'

Making requests with Credentials still validates with AWS if the credentials exist.

Checklist

  • [ x] I have read Caveats documentation and didn't find a solution for this problem there.

Bug description

When making a request to S3 using GetObjectCommand with credentials supplied, mocking said GetObjectCommand results in various errors like, these are the errors I would be expected from AWS if I provided invalid credentials. I shouldn't have to put production credentials for a mock:

[InvalidAccessKeyId: The AWS Access Key Id you provided does not exist in our records or Could not load credentials from any providers]

This happens when I do this.

s3Mock.on(ListObjectsV2Command).resolves({ Contents: [], });

await expect(readFromMarker(undefined, credentials)).resolves.toEqual([]);

Read from marker simply calls the listObjects command.

const listObjectsV2Command = new ListObjectsV2Command({ Bucket: bucketname, MaxKeys: 1000, StartAfter: marker, Prefix: prefix });
const client = new S3Client({ credentials, region });
return client.send(listObjectsV2Command);

This happens for any S3 Command.

Environment

  • Node version: 16.13.0
  • Testing lib and version: Jest
  • Typescript version: 4.5.2
  • AWS SDK v3 Client mock version: 0.5.6
  • AWS JS SDK libs and versions: 3.48.0

Typescript errors after upgrading from 0.5.3 to 0.5.4

Checklist

  • I have read Caveats documentation and didn't find a solution for this problem there.

Bug description

Firstly, thank you for making this library available - it's great and much appreciated.

The issue I'm encountering is that after upgrading 0.5.3 -> 0.5.4 (0.5.3 worked fine). I'm getting typescript errors such as:

error TS2339: Property 'Entries' does not exist on type 'ServiceInputTypes'.
Property 'Entries' does not exist on type 'AddPermissionCommandInput'.
94     expect(mockSqsClient.call(0).args[0].input.Entries).toHaveLength(10);
error TS2339: Property 'QueueUrl' does not exist on type 'ServiceInputTypes'.
Property 'QueueUrl' does not exist on type 'CreateQueueCommandInput'.
104     expect(mockSqsClient.call(2).args[0].input.QueueUrl).toBe(

There seems to be some type definitions mix up going on, because in this test I'm actually dealing with a SendMessageBatchCommandInput which should have QueueUrl/Entries available to it.

In case it helps with diagnosing, I am in a monorepo environment, but all packages have been upgrade to the latest version of their respective SDK deps (ie: 3.31.0 at time of writing) and the root level package.json has the following deps declared:

 "devDependencies": {
   "@aws-sdk/client-s3": "^3.31.0",
   "@aws-sdk/types": "^3.29.0",
   "aws-sdk-client-mock": "^0.5.4",
 }

Environment

  • Node version: 14.17.6
  • Typescript version: 4.4.3
  • AWS SDK v3 Client mock version: 0.5.4
  • AWS JS SDK libs and versions: 3.31.0 (3.29.0 for @aws-sdk/types ie: the latest)

Doesn't appear to be compatibility with latest version `v3.25.0`

I'm see compiler errors like the following when using v3.25.0 versions of the clients. Downgrading to v3.18.0 fixes the issue.

Argument of type 'typeof EventBridgeClient' is not assignable to parameter of type 'InstanceOrClassType<Client<ServiceInputTypes, MetadataBearer, any>>'.
  Type 'typeof EventBridgeClient' is not assignable to type 'ClassType<Client<ServiceInputTypes, MetadataBearer, any>>'.
    The types of 'prototype.middlewareStack.concat' are incompatible between these types.
      Type '<InputType extends ServiceInputTypes, OutputType extends ServiceOutputTypes>(from: MiddlewareStack<InputType, OutputType>) => MiddlewareStack<InputType, OutputType>' is not assignable to type '<InputType extends ServiceInputTypes, OutputType extends MetadataBearer>(from: MiddlewareStack<InputType, OutputType>) => MiddlewareStack<InputType, OutputType>'.
        Types of parameters 'from' and 'from' are incompatible.
          Type 'MiddlewareStack<InputType, OutputType>' is not assignable to type 'MiddlewareStack<InputType, ServiceOutputTypes>'.
            Types of property 'addRelativeTo' are incompatible.
              Type '(middleware: MiddlewareType<InputType, OutputType>, options: RelativeMiddlewareOptions) => void' is not assignable to type '(middleware: MiddlewareType<InputType, ServiceOutputTypes>, options: RelativeMiddlewareOptions) => void'.
                Types of parameters 'middleware' and 'middleware' are incompatible.
                  Type 'MiddlewareType<InputType, ServiceOutputTypes>' is not assignable to type 'MiddlewareType<InputType, OutputType>'.
                    Type 'InitializeMiddleware<InputType, ServiceOutputTypes>' is not assignable to type 'MiddlewareType<InputType, OutputType>'.
                      Type 'InitializeMiddleware<InputType, ServiceOutputTypes>' is not assignable to type 'InitializeMiddleware<InputType, OutputType>'.
                        Call signature return types 'InitializeHandler<InputType, ServiceOutputTypes>' and 'InitializeHandler<InputType, OutputType>' are incompatible.
                          Type 'Promise<InitializeHandlerOutput<ServiceOutputTypes>>' is not assignable to type 'Promise<InitializeHandlerOutput<OutputType>>'.
                            Type 'InitializeHandlerOutput<ServiceOutputTypes>' is not assignable to type 'InitializeHandlerOutput<OutputType>'.
                              Types of property 'output' are incompatible.
                                Type 'ServiceOutputTypes' is not assignable to type 'OutputType'.
                                  'ServiceOutputTypes' is assignable to the constraint of type 'OutputType', but 'OutputType' could be instantiated with a different subtype of constraint 'MetadataBearer'.
                                    Type 'ActivateEventSourceCommandOutput' is not assignable to type 'OutputType'.
                                      'ActivateEventSourceCommandOutput' is assignable to the constraint of type 'OutputType', but 'OutputType' could be instantiated with a different subtype of constraint 'MetadataBearer'.

Support v2-style calls

The AWS SDK v3 gives an option to use it similarly to v2 SDK, with command method call instead of send():

const {DynamoDB} = require('@aws-sdk/client-dynamodb');

const dymamoDB = new DynamoDB({region: 'us-west-2'});

const data = await dymamoDB.createTable({
    TableName : TABLE_NAME
});

Although this approach is not recommended by AWS, implement mocks for it as people may use is in a transition period.


Please vote with ๐Ÿ‘ ย  if you need this.

Trouble mocking Secrets Manager Client

Checklist

  • I have read Caveats documentation and didn't find a solution for this problem there.

Bug description

Mocking secrets manager client doesn't work as the client first tries to validate a security token which is probably not using the mocked .send command. Example below;

import { mockClient } from 'aws-sdk-client-mock';
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';

const smMock = mockClient(new SecretsManagerClient({}));

smMock.on(GetSecretValueCommand)
    .resolves({ 
        SecretString: JSON.stringify({ my_secret_key: 'my_secret_value' }) 
    });

This code throws:

ExpiredTokenException: The security token included in the request is expired

I've tried adding a .callsFake(() => console.log('Hit the mock')) before the .resolves... method and it's not even getting that far (which supports the theory that the token validation doesn't use send).

Environment

  • Node version: 16.4.1
  • Typescript version: 4.3.5
  • AWS SDK v3 Client mock version: 0.5.3
  • AWS JS SDK libs and versions:
    • @aws-sdk/client-secrets-manager 3.28.0

S3 Presign

S3 presign is done using a helper library @aws-sdk/s3-request-presigner this seems like something that would need a helper library or additional documentation to mock properly.

It would be good to see a way to mock presign requests. I'm happy to help with this.

Allow throwing errors in callsFake() mock

Currently, the callsFake() mock does not support throwing an error from it. The error should be returned as a Client send() error, in the same way as mock.rejects() behaves.


Please vote with ๐Ÿ‘ ย  if you need this.

Some typescript types are wrong for RDSClient

Node version: 14.17.1
Typescript: 4.2.4
aws-sdk/client-rds: 3.18.0
aws-sdk-client-mock: 0.5.3

Code to reproduce:

import { RDSClient } from '@aws-sdk/client-rds';
import { mockClient } from "aws-sdk-client-mock";
let rdsClientMock = mockClient(RDSClient);

Getting an error when trying to run this code:

zhenya@LO-0473 database-pipeline % yarn jest ./test/unit/theTest.test.ts
yarn run v1.22.10
$ /Users/zhenya/Desktop/projects/database-pipeline/node_modules/.bin/jest ./test/unit/lambdas/theTest.test.ts
 FAIL  test/unit/lambdas/theTest.test.ts
  โ— Test suite failed to run

    test/unit/lambdas/theTest.test.ts:3:32 - error TS2345: Argument of type 'typeof RDSClient' is not assignable to parameter of type 'InstanceOrClassType<Client<ServiceInputTypes, MetadataBearer, any>>'.
      Type 'typeof RDSClient' is not assignable to type 'ClassType<Client<ServiceInputTypes, MetadataBearer, any>>'.
        The types of 'prototype.middlewareStack.concat' are incompatible between these types.
          Type '<InputType extends ServiceInputTypes, OutputType extends ServiceOutputTypes>(from: MiddlewareStack<InputType, OutputType>) => MiddlewareStack<InputType, OutputType>' is not assignable to type '<InputType extends ServiceInputTypes, OutputType extends MetadataBearer>(from: MiddlewareStack<InputType, OutputType>) => MiddlewareStack<InputType, OutputType>'.
            Types of parameters 'from' and 'from' are incompatible.
              Type 'MiddlewareStack<InputType, OutputType>' is not assignable to type 'MiddlewareStack<InputType, ServiceOutputTypes>'.
                Types of property 'addRelativeTo' are incompatible.
                  Type '(middleware: MiddlewareType<InputType, OutputType>, options: RelativeMiddlewareOptions) => void' is not assignable to type '(middleware: MiddlewareType<InputType, ServiceOutputTypes>, options: RelativeMiddlewareOptions) => void'.
                    Types of parameters 'middleware' and 'middleware' are incompatible.
                      Type 'MiddlewareType<InputType, ServiceOutputTypes>' is not assignable to type 'MiddlewareType<InputType, OutputType>'.
                        Type 'InitializeMiddleware<InputType, ServiceOutputTypes>' is not assignable to type 'MiddlewareType<InputType, OutputType>'.
                          Type 'InitializeMiddleware<InputType, ServiceOutputTypes>' is not assignable to type 'InitializeMiddleware<InputType, OutputType>'.
                            Call signature return types 'InitializeHandler<InputType, ServiceOutputTypes>' and 'InitializeHandler<InputType, OutputType>' are incompatible.
                              Type 'Promise<InitializeHandlerOutput<ServiceOutputTypes>>' is not assignable to type 'Promise<InitializeHandlerOutput<OutputType>>'.
                                Type 'InitializeHandlerOutput<ServiceOutputTypes>' is not assignable to type 'InitializeHandlerOutput<OutputType>'.
                                  Types of property 'output' are incompatible.
                                    Type 'ServiceOutputTypes' is not assignable to type 'OutputType'.
                                      'ServiceOutputTypes' is assignable to the constraint of type 'OutputType', but 'OutputType' could be instantiated with a different subtype of constraint 'MetadataBearer'.
                                        Type 'AddRoleToDBClusterCommandOutput' is not assignable to type 'OutputType'.
                                          'AddRoleToDBClusterCommandOutput' is assignable to the constraint of type 'OutputType', but 'OutputType' could be instantiated with a different subtype of constraint 'MetadataBearer'.

    3 let rdsClientMock = mockClient(RDSClient);
                                      ~~~~~~~~~

Conflicting auto-installed peer dependencies with NPM v7

From v0.5.4 the lib contains a utility to mock @aws-sdk/lib-storage. This utility automatically mocks some commands for S3Client.

To make this work, the library specifies @aws-sdk/client-s3 as a peer dependency.

With NPM 6 and YARN, peer dependency will not be installed. If the client wants to use lib-storage and mock it, he needs to install @aws-sdk/client-s3 anyway. When specifying @aws-sdk/client-s3 as a dependency, this package needs to be in the same version as other @aws-sdk/client-* packages.

However, NPM 7 will automatically install @aws-sdk/client-s3, even if the client does not need it. Because the version restriction is ^3.0.0, it will install the latest @aws-sdk/client-s3 version available.

If the client uses other @aws-sdk/client-* packages in older versions, now it will have conflicting types installed.

For now, to resolve it, the best way of action is to specify @aws-sdk/client-s3 as a dependency manually, with the same version as other SDK packages. Alternatively, specifying @aws-sdk/types may work as well.

I will work on resolving this on the library side. However, I was unable to do this so far. What I'm looking for right now, is a way to provide S3Client mocks without specifying @aws-sdk/client-s3 as a (regular or peer) dependency.

Package installation requires cmake binary

Checklist

  • [ x] I have read Caveats documentation and didn't find a solution for this problem there.

Bug description

A dependency requires aws-crt, which in turn requires cmake-js, which requires a local cmake installation. Which together makes this package harder to sell to the rest of the team.

Could you please list this requirement in the Install doc?

Relevant output from npm install -D aws-sdk-client-mock -ddd (/path/to/project replaces sensitive information):

npm ERR! path /path/to/project/node_modules/aws-crt
npm ERR! command failed
npm ERR! command sh -c node ./scripts/install.js
npm ERR! info TOOL Using Unix Makefiles generator.
npm ERR! info TOOL Building only the install target, as specified from the command line.
npm ERR! ERR! OMG CMake executable is not found. Please use your system's package manager to install it, or you can get installers from there: http://cmake.org.
npm ERR! /path/to/project/node_modules/cmake-js/lib/cMake.js:117
npm ERR!         throw new Error("CMake executable is not found. Please use your system's package manager to install it, or you can get installers from there: http://cmake.org.");
npm ERR!               ^
npm ERR!
npm ERR! Error: CMake executable is not found. Please use your system's package manager to install it, or you can get installers from there: http://cmake.org.
npm ERR!     at CMake.verifyIfAvailable (/path/to/project/node_modules/cmake-js/lib/cMake.js:117:15)
npm ERR!     at CMake.build (/path/to/project/node_modules/cmake-js/lib/cMake.js:287:10)
npm ERR!     at BuildSystem._invokeCMake (/path/to/project/node_modules/cmake-js/lib/buildSystem.js:70:40)
npm ERR!     at async buildFromRemoteSource (/path/to/project/node_modules/aws-crt/scripts/build.js:148:5)

Environment

  • Node version: v16.13.0
  • Testing lib and version: [email protected]
  • Typescript version: 4.4.3
  • AWS SDK v3 Client mock version: 0.5.6, presumably
  • AWS JS SDK libs and versions:
    • @aws-sdk/client-sqs: 3.43.0
  • Platform: macOS 12.1, Apple M1 Pro chipset

Typed calls to access returnValue and args

I'm wondering if you would be interested in adding a method or helper function to return a typed Sinon Spy? This is useful when testing parameters and/or return value.

I produced a function that seems to work (but I'm pretty sure it's not the best way to do it):

import { Command } from '@aws-sdk/types';
import { AwsStub } from 'aws-sdk-client-mock';

export function isAwsCall<
  T extends Command<any, any, any, any, any>,
  TInput extends T extends Command<infer A, infer B, infer C, infer D, infer E> ? A : never,
  TOutput extends T extends Command<infer A, infer B, infer C, infer D, infer E> ? C : never,
  TCmdInput extends T extends Command<infer A, infer B, infer C, infer D, infer E> ? B : never,
  TCmdOutput extends T extends Command<infer A, infer B, infer C, infer D, infer E> ? D : never,
>(
  call: sinon.SinonSpyCall<[Command<TInput, any, TOutput, any, any>], any>,
  commandType: new (input: TCmdInput) => T,
): call is sinon.SinonSpyCall<[T], TCmdOutput> {
  return call.args[0] instanceof commandType;
}

export function filterAwsCalls<
  T extends Command<any, any, any, any, any>,
  TInput extends T extends Command<infer A, infer B, infer C, infer D, infer E> ? A : never,
  TOutput extends T extends Command<infer A, infer B, infer C, infer D, infer E> ? C : never,
  TCmdInput extends T extends Command<infer A, infer B, infer C, infer D, infer E> ? B : never,
  TCmdOutput extends T extends Command<infer A, infer B, infer C, infer D, infer E> ? D : never,
>(
  awsClientStub: AwsStub<TInput, TOutput>,
  commandType: new (input: TCmdInput) => T,
): sinon.SinonSpyCall<[T], TCmdOutput>[] {
  return awsClientStub.calls().filter((call): call is sinon.SinonSpyCall<[T], TCmdOutput> => {
    return isAwsCall(call, commandType);
  });
}

And then you can access the args and returnValue with the good type:

const commands = filterAwsCalls(sqsClientStub, DeleteMessageBatchCommand);
expect(commands[0].args[0].input.Entries).toHaveLength(10);

Thanks for this awesome lib.

Typescript error when mocking EC2

when mocking EC2

const ec2ClientMock = mockClient(EC2Client);
const ec2 = new EC2Client({ region: "eu-west-1" });

I'm getting this error:


Argument of type 'typeof EC2Client' is not assignable to parameter of type 'InstanceOrClassType<Client<ServiceInputTypes, MetadataBearer, any>>'.
  Type 'typeof EC2Client' is not assignable to type 'ClassType<Client<ServiceInputTypes, MetadataBearer, any>>'.
    The types of 'prototype.middlewareStack.concat' are incompatible between these types.
      Type '<InputType extends ServiceInputTypes, OutputType extends ServiceOutputTypes>(from: MiddlewareStack<InputType, OutputType>) => MiddlewareStack<InputType, OutputType>' is not assignable to type '<InputType extends ServiceInputTypes, OutputType extends MetadataBearer>(from: MiddlewareStack<InputType, OutputType>) => MiddlewareStack<InputType, OutputType>'.
        Types of parameters 'from' and 'from' are incompatible.
          Type 'MiddlewareStack<InputType, OutputType>' is not assignable to type 'MiddlewareStack<InputType, ServiceOutputTypes>'.
            Types of property 'addRelativeTo' are incompatible.
              Type '(middleware: MiddlewareType<InputType, OutputType>, options: RelativeMiddlewareOptions) => void' is not assignable to type '(middleware: MiddlewareType<InputType, ServiceOutputTypes>, options: RelativeMiddlewareOptions) => void'.
                Types of parameters 'middleware' and 'middleware' are incompatible.
                  Type 'MiddlewareType<InputType, ServiceOutputTypes>' is not assignable to type 'MiddlewareType<InputType, OutputType>'.
                    Type 'InitializeMiddleware<InputType, ServiceOutputTypes>' is not assignable to type 'MiddlewareType<InputType, OutputType>'.
                      Type 'InitializeMiddleware<InputType, ServiceOutputTypes>' is not assignable to type 'InitializeMiddleware<InputType, OutputType>'.
                        Call signature return types 'InitializeHandler<InputType, ServiceOutputTypes>' and 'InitializeHandler<InputType, OutputType>' are incompatible.
                          Type 'Promise<InitializeHandlerOutput<ServiceOutputTypes>>' is not assignable to type 'Promise<InitializeHandlerOutput<OutputType>>'.
                            Type 'InitializeHandlerOutput<ServiceOutputTypes>' is not assignable to type 'InitializeHandlerOutput<OutputType>'.
                              Types of property 'output' are incompatible.
                                Type 'ServiceOutputTypes' is not assignable to type 'OutputType'.
                                  'ServiceOutputTypes' is assignable to the constraint of type 'OutputType', but 'OutputType' could be instantiated with a different subtype of constraint 'MetadataBearer'.
                                    Type 'MetadataBearer' is not assignable to type 'OutputType'.
                                      'MetadataBearer' is assignable to the constraint of type 'OutputType', but 'OutputType' could be instantiated with a different subtype of constraint 'MetadataBearer'.

Using latest version 0.3.1

Issues with RDSClient

First off, awesome project -- I'm looking forward to seeing it mature.

I created this thin RDS client wrapper as an experiment:

import { RDSClient } from '@aws-sdk/client-rds';

class RDSClientWrapper {
    private client: RDSClient;

    constructor(client: RDSClient) {
        this.client = client;
    }
}

In the testing file, I have this:

import { RDSClient } from '@aws-sdk/client-rds';
import { mockClient } from 'aws-sdk-client-mock';
...
describe('RDSClientWrapper', function() {
    let rdsClientWrapper: RDSClientWrapper;
    beforeEach(function() {
        const mockRDS = mockClient(RDSClient);
        rdsClientWrapper = new RDSClientWrapper(mockRDS);
    });
...
}

This line rdsClientWrapper = new RDSClientWrapper(mockRDS); errors with:

Argument of type 'AwsStub<ServiceInputTypes, ServiceOutputTypes>' is not assignable to parameter of type 'RDSClient'.
Type 'AwsStub<ServiceInputTypes, ServiceOutputTypes>' is missing the following properties from type 'RDSClient': config, destroy, middlewareStackts(2345)

Is extra work needed to mock the RDSClient?

Mocking a specific command only

Apologies if I'm missing something obvious here but is there a way for me to only mock a specific command and for the client to use the real behaviour of the client of all the other commands? For a bit of context, I have a bunch of tests that run against dynamodb-local. I am trying to test a function that uses the batchWrite command but the function I'm testing makes other calls to dynamo before that. I would like to only mock the batchWrite function and allow the other commands to pass through to the local DB using the actual SDK. Is this possible?

Set SDK Instance

First off: Awesome job!

This is more a question/thought than a feature request and is totally possible that I'm missing something on my end ๐Ÿ˜„.

The aws-sdk-mock library has setSDK and setSDKInstance functions. I'm wondering if that is something you've considered implementing?

Use case

The use case I have for it is that in some "serverless" projects is that sometimes they have a central tests directory and the lambda handlers in other directories with their own package.json files.

tests/
  getItem.test.js
src/
  getItems/
	index.js.  <--  @aws-sdk
	package.json
package.json  <--- jest / npm run test

This does not work (Resource not found error I think I got) but if I move the test to the same places as the handler it the mocking works.

src/
  getItems/
	index.js.  <--  @aws-sdk
	getItem.test.js
	package.json
package.json  <--- jest / npm run test

Is it clear what I mean. I'm happy to try to help if you think its a good, doable idea

Matchers returning undefined

When adding input matchers the mocked response is undefined

Works

    ssmClient
      .on(GetParameterCommand)
     .resolves({ Parameter: { Value: 'something' } }

Doesn't work:

    ssmClient
      .on(GetParameterCommand, { Name: '/config', WithDecryption: true })
      .resolves({ Parameter: { Value: 'something' } }

When I log out the response for the ssm call in the application code 'something' is logged out both times.

Thanks for the awesome library!

Move @types/sinon to prod dependencies

Building with Typescript on a project that doesn't already use sinon throws this error:

node_modules/aws-sdk-client-mock/dist/types/awsClientStub.d.ts:2:41 - error TS7016: Could not find a declaration file for module 'sinon'. '<my-project>/node_modules/sinon/lib/sinon.js' implicitly has an 'any' type.
  Try `npm install @types/sinon` if it exists or add a new declaration (.d.ts) file containing `declare module 'sinon';`

2 import { SinonSpyCall, SinonStub } from 'sinon';
                                          ~~~~~~~

The Typescript handbook recommends that any exported type that has a dependency on a package should be in the dependencies rather than devDependencies. Moving @types/sinon to the dependencies would solve this issue.

sinon_1.stub is not a function

Checklist

  • I have read Caveats documentation and didn't find a solution for this problem there.

Bug description

Have project setup to use Jest with ts-jest preset.

  1. Used yarn add -D aws-sdk-client-mock to install the package to our project
  2. Created a simple test to try out the mock functionality.
  3. When running the test with const sqsMock = mockClient(SQSClient), encountered the error TypeError: sinon_1.stub is not a function

Navigating to the implementation of mockClient, the import shows that stub is being pulled in from sinon.ts in the aws-sdk-client-mock package itself instead of from sinon installed in node_modules, so an error is thrown. See reference:

aws-sdk-client-mock-wrong-import

The test was very simply:

import { SQSClient } from "@aws-sdk/client-sqs";
import { mockClient } from "aws-sdk-client-mock";

describe('Simple Test', () => {
  const sqsMock = mockClient(SQSClient);

  it('should exist', () => {
    expect(true).toBeTruthy();
  });
});

This was tried in two different projects (both using yarn) and on several different computers, all encountered the same issue.

Environment

  • Node version: 16.8
  • Typescript version: 4.3.5
  • AWS SDK v3 Client mock version: 0.5.5
  • AWS JS SDK libs and versions: @aws-sdk/client-sqs v3.31.0
  • Yarn 1.22.10

Using mocks in multiple test files fails

Checklist

  • I have read Caveats documentation and didn't find a solution for this problem there.

Bug description

If we have mocks in multiple files, some of the test suites are failing.
If we run each file individually the tests succeed.
If we run mocha with --parallel it also works

See repro here: https://github.com/danmana/mocha-aws-sdk-client-mock

npm run mocha -- tests/one*.js works

npm run mocha -- tests/two*.js works

npm run mocha -- tests/*.js โš ๏ธ does not work, suite one fails

npm run mocha -- tests/*.js --parallel works
Also if we put all tests in the same file, it works.

The tests are pretty straight forward

const { ListBucketsCommand, S3Client } = require('@aws-sdk/client-s3');
const { mockClient } = require('aws-sdk-client-mock');
const assert = require('assert');
const mock = mockClient(S3Client);

describe('one', () => {
  let client;

  beforeEach(() => {
    mock.reset();
    client = new S3Client();
  });

  it('should work', async () => {
    mock.on(ListBucketsCommand).resolves({});

    const result = await client.send(new ListBucketsCommand());

    assert.deepEqual(result, {});
  });

});

This is probably due to the way mocha runs the tests, but I can't figure out how to fix it.

Environment

  • Node version: v14.18.0
  • Typescript version: 6.14.15
  • AWS SDK v3 Client mock version:
  • AWS JS SDK libs and versions:
  • "@aws-sdk/client-s3": "^3.41.0",
  • "aws-sdk-client-mock": "^0.5.6",

`rest()` and `resetBehavior()` stops `onAnyCommand()` working (DynamoDB)

Checklist

  • I have read Caveats documentation and didn't find a solution for this problem there.

Bug description

When using the reset() function between tests and using onAnyCommand() to set a default response there is no error however the default response does not seem to work.

As part of debugging the issue it was noticed in the comments that reset() basically performs resetHistory() and resetBehavior(). We tried setting these individually and (as expected) were able to pin it down to just the resetBehavior() function.

Rightly or wrongly to avoid asserting the arguments of the function call I was using the onAnyCommand().rejects() to reject any calls by default and then using more specific on() to catch the calls I was expecting.
This all works but when it came to mutation testing and deliberately trying to force tests to fail if the code isn't as expected the tests were passing when in specific orders.
The first test would use the onAnyCommand() response as expected, between tests (using beforeEach()) we use reset() to remove any behaviours and history and then do onAnyCommand().rejects() again, however in subsequent tests we do not see this being used and instead the mock resolves the promise.

In the below we expect both tests to pass which they do, however if we change the service name on L30 to something else, NOT-my-service for example, the test passes when it should fail. Run the same test in isolation and it fails as expected.
(for clarity this is a slightly simplified version to help convey the issue, we are making mutations in the .ts not the .test.ts which is why this may seem an odd way of doing things!)

// bug-report.test.ts
import {mockClient} from 'aws-sdk-client-mock'
import {DeleteItemCommand, DynamoDBClient} from '@aws-sdk/client-dynamodb'
import {removeLock} from './bug-report'

const ddbMock = mockClient(DynamoDBClient)
const WrongParamsErr = new Error('Incorrect call params (without this mock will resolve any call!)')

describe('Remove lock', () => {
  beforeEach(() => {
    ddbMock.reset()
    ddbMock.onAnyCommand().rejects(WrongParamsErr)
  })

  test('throws decorated error if delete command fails', async () => {
    expect.assertions(1)
    const err = new Error('mock failed error')
    ddbMock.on(DeleteItemCommand).rejects(err)
    await expect(removeLock('my-service')).rejects.toThrow(`Failed removing lock from DDB (my-service): ${err}`)
  })

  test('makes expected calls to DDB SDK and logs', async () => {
    expect.assertions(1)
    const args = {
      Key: {lock: {S: 'NOT-my-service'}},
      TableName: 'my-table'
    }
    ddbMock
      .on(DeleteItemCommand, args)
      .resolves({})
    await expect(removeLock('my-service')).resolves.not.toThrow()
  })
})
// bug-resport.ts
import {DeleteItemCommand, DynamoDBClient} from '@aws-sdk/client-dynamodb'

const DDB = new DynamoDBClient({region: 'eu-central-1'})

export async function removeLock(serviceName: string): Promise<void> {
  try {
    const command = new DeleteItemCommand({
      Key: {lock: {S: serviceName}},
      TableName: 'my-table'
    })
    await DDB.send(command)
  } catch (err) {
    throw new Error(`Failed removing lock from DDB (${serviceName}): ${err}`)
  }
}

Final few observations, if we switch the order of the tests then the mutation fails as expected.
Also strangely if we use the below as our describe block, both tests pass as expected, if we change the assertion to removeLock('NOT-my-service'), the test fails, as expected. Yet changing the mock to Key: {lock: {S: 'NOT-my-service'}}, leaving the assertion as the original removeLock('my-service') the test passes when the same fail is expected.

describe('Remove lock', () => {
  beforeEach(() => {
    ddbMock.reset()
    ddbMock.onAnyCommand().rejects(WrongParamsErr)
  })

  test('makes expected calls to DDB SDK and logs', async () => {
    expect.assertions(1)
    const args = {
      Key: {lock: {S: 'my-service'}},
      TableName: 'my-table'
    }
    ddbMock
      .on(DeleteItemCommand, args)
      .resolves({})
    await expect(removeLock('my-service')).resolves.not.toThrow()
  })

  test('makes expected calls to DDB SDK and logs', async () => {
    expect.assertions(1)
    const args = {
      Key: {lock: {S: 'my-service'}},
      TableName: 'my-table'
    }
    ddbMock
      .on(DeleteItemCommand, args)
      .resolves({})
    await expect(removeLock('my-service')).resolves.not.toThrow()
  })
})

Environment

  • Node version: v12.22.7
  • Testing lib and version: Jest 27.4.5
  • Typescript version: 4.5.4
  • AWS SDK v3 Client mock version: 0.5.6
  • AWS JS SDK libs and versions:
    • @aws-sdk/client-dynamodb v3.44.0

Unrelated tests are bleeding into each other

Checklist

  • I have read Caveats documentation and didn't find a solution for this problem there.

Bug description

Unrelated tests are bleeding data into each other. Both tests pass when run separately. When they are run together, "returns a generic error when dynamodb fails to delete" fails consistently with a "fake ddb scene error" that only exists in the other unrelated test. My current workaround is to put the second test above the first, which resolves it. This is likely related to the caveats in some way, though it is peculiar behavior because it seems the mock resets are ignored or don't matter. When I don't use module-level mock, and put an individual in each tests, the same problem occurs. There is an implicit global state that is hard to reason about.

Environment

  • Node version: 14.18.1
  • Typescript version: 4.4.4
  • AWS SDK v3 Client mock version: 0.5.6
  • AWS JS SDK libs and versions: @aws-sdk/* 3.41.0

Here is an example test file, in case I am doing something obviously dumb:

import {
  CloudControlClient,
  DeleteResourceCommand,
  GetResourceRequestStatusCommand,
} from '@aws-sdk/client-cloudcontrol';
import { AttributeValue, DeleteItemCommand, DynamoDBClient, GetItemCommand } from '@aws-sdk/client-dynamodb';
import { mockClient } from 'aws-sdk-client-mock';
import faker from 'faker';
import { ulid } from 'ulid';

import { NodeNotFoundError, SceneNotFoundError } from '../../lib/errors';
import { handler } from '../nodeDelete';

const ddbMock = mockClient(DynamoDBClient);
const ccMock = mockClient(CloudControlClient);

function getDdbSceneOutput(accountId: string, sceneId: string): { [key: string]: AttributeValue } {
  return {
    accountId: { S: accountId },
    apiKey: { S: faker.internet.password() },
    createdAt: { S: faker.date.recent().toISOString() },
    dataPlaneAccountId: { S: faker.internet.password() },
    eventBus: { S: faker.internet.password() },
    id: { S: sceneId },
    managementRole: { S: faker.internet.password() },
    realtimeEventWsUrl: { S: faker.internet.password() },
    runtimeRole: { S: faker.internet.password() },
    status: { S: 'RUNNING' },
  };
}

function getDdbNodeOutput(sceneId: string, nodeId: string): { [key: string]: AttributeValue } {
  return {
    awsIdentifier: { S: faker.internet.password() },
    awsOperationStatus: { S: faker.internet.password() },
    awsState: { S: '{}' },
    awsTypeName: { S: 'AWS::Lambda::Function' },
    createdAt: { S: faker.date.recent().toISOString() },
    id: { S: nodeId },
    name: { S: faker.name.jobDescriptor() },
    sceneId: { S: sceneId },
    state: { S: 'RUNNING' },
  };
}

describe('tests nodeDelete', () => {
  beforeEach(() => {
    ddbMock.reset();
    ccMock.reset();
  });

  test('returns a node not found error when dynamo fails to return a node', async () => {
    const accountId = ulid();
    const sceneId = ulid();
    const nodeId = ulid();
    const tableName = 'fakeTableName';
    const event = { accountId, nodeId, sceneId, tableName };
    ddbMock
      .on(GetItemCommand, {
        Key: {
          PK: { S: `ACCOUNT#${accountId}` },
          SK: { S: `SCENE#${sceneId}` },
        },
        TableName: tableName,
      })
      .rejects(new Error('fake ddb scene get error'))
      .on(GetItemCommand, {
        Key: {
          PK: { S: `ACCOUNT#${accountId}|SCENE#${sceneId}` },
          SK: { S: `NODE#${nodeId}` },
        },
        TableName: tableName,
      })
      .resolves({});
    await expect(handler(event)).rejects.toThrow(NodeNotFoundError);
  });

  test('returns a generic error when dynamodb fails to delete', async () => {
    const accountId = ulid();
    const sceneId = ulid();
    const nodeId = ulid();
    const tableName = 'fakeTableName';
    const event = { accountId, nodeId, sceneId, tableName };
    ddbMock
      .on(GetItemCommand, {
        Key: {
          PK: { S: `ACCOUNT#${accountId}` },
          SK: { S: `SCENE#${sceneId}` },
        },
        TableName: tableName,
      })
      .resolves({ Item: getDdbSceneOutput(nodeId, sceneId) })
      .on(GetItemCommand, {
        Key: {
          PK: { S: `ACCOUNT#${accountId}|SCENE#${sceneId}` },
          SK: { S: `NODE#${nodeId}` },
        },
        TableName: tableName,
      })
      .resolves({ Item: getDdbNodeOutput(nodeId, sceneId) })
      .on(DeleteItemCommand, {
        Key: {
          PK: { S: `ACCOUNT#${accountId}|SCENE#${sceneId}` },
          SK: { S: `NODE#${nodeId}` },
        },
        TableName: tableName,
      })
      .rejects(new Error('fake ddb node delete error'));
    ccMock
      .on(DeleteResourceCommand)
      .resolves({ ProgressEvent: {} })
      .on(GetResourceRequestStatusCommand)
      .resolves({ ProgressEvent: {} });
    await expect(handler(event)).rejects.toThrow('fake ddb node delete error');
  });
});

Type support for DynamoDB DocumentClient

AWS SDK v3.8.0 brings support for DynamoDB DocumentClient.

It can be mocked just like any other AWS SDK Client:

import {DynamoDBDocumentClient, PutCommand} from '@aws-sdk/lib-dynamodb';

const dynamoDB = mockClient(DynamoDBDocumentClient);

dynamoDB.on(PutCommand).resolves({
    // ...
});

This works, but DynamoDBDocumentClient type does not match expected type for mockClient() function and currently the problem must be silenced with // @ts-ignore comment.

Update the expected type to support high-level clients like this one.


Please vote with ๐Ÿ‘ ย  if you need this.

Allow partial payload match

As I understand, currently payload matches are strict. However, sometimes we care only about a single field (e.g. some id or name) out of otherwise large objects. Writing these full objects creates bloated test codes.

It would be great to have the option to define partial matchers for payloads.

Mock.resolves is returning undefined with specific input on `UpdateItemCommand`

I have the following spec file which I am running with jest ^27.4.7. This files is testing a nodeV14 lambda function that handles updating users and their farms.

When mocking the UpdateItemCommand, this is returning undefined with specific input.

const { mockClient } = require("aws-sdk-client-mock");
const {
  DynamoDBClient,
  QueryCommand,
  BatchWriteItemCommand,
  UpdateItemCommand,
} = require("@aws-sdk/client-dynamodb");
const { marshall } = require("@aws-sdk/util-dynamodb");
const {
  CognitoIdentityProviderClient,
  AdminGetUserCommand,
} = require("@aws-sdk/client-cognito-identity-provider");
const { getFarmsOfUser, updateUserFarm, updateUser } = require("./dbCommands");
const { getUserEmail } = require("./cognitoCommands");
const { handler } = require("./index");

const dbMock = mockClient(DynamoDBClient);
const cgMock = mockClient(CognitoIdentityProviderClient);

const fakeUuid = "21153f1a-0345-4ea2-bbd2-0e7d16922b29";

jest.mock("uuid", () => ({
  v4: () => fakeUuid,
}));

describe("update-users", () => {
  const OLD_ENV = process.env;
  const email = "[email protected]";
  const date = "22022-01-29T00:00:00.000Z";
  const userPoolId = "user-pool-id";
  const userFarmsTableName = "user-farm-table";
  const usersTableName = "users-table";

  function calcExprAttrVal(farmIds, updatedAt) {
    const marshalled = marshall({ farmIds, updatedAt });
    const expressionAttVal = {};
    for (const [key, value] of Object.entries(marshalled)) {
      expressionAttVal[`:${key}`] = value;
    }
    return expressionAttVal;
  }

  beforeAll(() => {
    jest.useFakeTimers("modern");
    jest.setSystemTime(new Date(2022, 0, 29));
  });

  beforeEach(() => {
    jest.resetModules(); // Most important - it clears the cache
    process.env = { ...OLD_ENV }; // Make a copy
  });

  afterAll(() => {
    process.env = OLD_ENV; // Restore old environment
    jest.useRealTimers();
  });

  describe("index", () => {
    const event = {
      users: [
        { id: "1", farmIds: ["1", "2"] },
        { id: "2", farmIds: ["3"] },
      ],
    };

    beforeEach(() => {
      dbMock.reset();
      cgMock.reset();
      process.env.USER_POOL_ID = userPoolId;
      process.env.USER_FARMS_TABLE = userFarmsTableName;
      process.env.USERS_TABLE_NAME = usersTableName;
    });

    it("should add a farm to user 2 and remove and add a farm from user 1", async () => {
      const firstGetFarmInput = {
        TableName: userFarmsTableName,
        KeyConditionExpression: `#key = :val`,
        IndexName: "user-id-index",
        ExpressionAttributeNames: {
          "#key": "userId",
        },
        ExpressionAttributeValues: {
          ":val": {
            S: "1",
          },
        },
      };

      const secondGetFarmInput = {
        TableName: userFarmsTableName,
        KeyConditionExpression: `#key = :val`,
        IndexName: "user-id-index",
        ExpressionAttributeNames: {
          "#key": "userId",
        },
        ExpressionAttributeValues: {
          ":val": {
            S: "2",
          },
        },
      };

      const firstUpdateUsrIpt = {
        TableName: usersTableName,
        Key: {
          id: { S: "1" },
        },
        UpdateExpression: `SET #F=:farmIds, #U=:updatedAt`,
        ExpressionAttributeNames: {
          "#F": "farmIds",
          "#U": "updatedAt",
        },
        ExpressionAttributeValues: calcExprAttrVal(["1", "2"], date),
        ReturnValues: "ALL_NEW",
      };

      const secondUpdateUsrIpt = {
        TableName: usersTableName,
        Key: {
          id: { S: "2" },
        },
        UpdateExpression: `SET #F=:farmIds, #U=:updatedAt`,
        ExpressionAttributeNames: {
          "#F": "farmIds",
          "#U": "updatedAt",
        },
        ExpressionAttributeValues: calcExprAttrVal(["3"], date),
        ReturnValues: "ALL_NEW",
      };

      cgMock.on(AdminGetUserCommand).resolves({
        UserAttributes: [{ Name: "email", Value: email }],
      });

      dbMock
        .on(QueryCommand, { ...firstGetFarmInput })
        .resolves({
          Items: [
            marshall({ farmId: "3", userId: "1", id: "22" }),
            marshall({ farmId: "1", userId: "1", id: "33" }),
          ],
        })
        .on(QueryCommand, { ...secondGetFarmInput })
        .resolves({ Items: [] });

      dbMock.on(BatchWriteItemCommand).resolves();

      const expOne = { id: "1", farmIds: ["1", "2"], email, updatedAt: date };
      const expTwo = { id: "2", farmIds: ["3"], email, updatedAt: date };

      // FAILING COMMANDS
      dbMock
        .on(UpdateItemCommand, { ...firstUpdateUsrIpt })
        .resolves({ Attributes: marshall(expOne) })
        .on(UpdateItemCommand, { ...secondUpdateUsrIpt })
        .resolves({ Attributes: marshall(expTwo) });

      const expected = [expOne, expTwo];
      const res = await handler(event);

      expect(expected).toEqual(res);
    });
  });
});

My index file:

const { getFarmsOfUser, updateUserFarm, updateUser } = require("./dbCommands");
const { getUserEmail } = require("./cognitoCommands");
const { defineToAdd, defineToRemove } = require("./utils");

exports.handler = async (event) => {
  console.log(`Event ${JSON.stringify(event, null, 2)}`);
  try {
    const { users } = event;
    return await Promise.all(
      users.map(async (data) => {
        let { farmIds, id } = data;
        const date = new Date();
        const updatedAt = date.toISOString();

        const existingFarms = await getFarmsOfUser(id);

        if (!Array.isArray(farmIds) || !farmIds || !farmIds.length) {
          farmIds = [];
        }

        const toDelete = defineToRemove(existingFarms, farmIds);
        const toAdd = defineToAdd(farmIds, existingFarms);

        await updateUserFarm(id, toAdd, toDelete, updatedAt);
        const userEmail = await getUserEmail(id);

        if (!toAdd.length && !toDelete.length) {
          data.email = userEmail;
          return data;
        }

        const user = await updateUser(id, farmIds, updatedAt);
        user.email = userEmail;
        return user;
      })
    );
  } catch (err) {
    console.error(`Error updating user: ${err}`);
    throw err;
  }
};

My dbCommands:

const {
  DynamoDBClient,
  QueryCommand,
  BatchWriteItemCommand,
  UpdateItemCommand,
} = require("@aws-sdk/client-dynamodb");
const { marshall, unmarshall } = require("@aws-sdk/util-dynamodb");
const { v4: uuid } = require("uuid");

const { AWS_REGION } = process.env;

const client = new DynamoDBClient({ region: AWS_REGION });

async function getFarmsOfUser(userId) {
  const { USER_FARMS_TABLE } = process.env;
  const input = {
    TableName: USER_FARMS_TABLE,
    KeyConditionExpression: `#key = :val`,
    IndexName: "user-id-index",
    ExpressionAttributeNames: {
      "#key": "userId",
    },
    ExpressionAttributeValues: {
      ":val": {
        S: userId,
      },
    },
  };
  const command = new QueryCommand(input);
  const { Items } = await client.send(command);
  return Items.length ? Items.map((item) => unmarshall(item)) : [];
}

async function updateUserFarm(userId, toAdd, toDelete, updatedAt) {
  const { USER_FARMS_TABLE } = process.env;
  let reqs = [];

  if (toAdd.length) {
    const putReqs = toAdd.reduce((acc, farmId) => {
      const id = uuid();
      const newItem = {
        id,
        farmId,
        userId,
        createdAt: updatedAt,
      };
      acc.push({ PutRequest: { Item: marshall(newItem) } });
      return acc;
    }, []);

    reqs = reqs.concat(putReqs);
  }

  if (toDelete.length) {
    const deleteReqs = toDelete.reduce((acc, id) => {
      acc.push({ DeleteRequest: { Key: marshall({ id }) } });
      return acc;
    }, []);

    reqs = reqs.concat(deleteReqs);
  }

  if (!reqs.length) {
    return Promise.resolve();
  }

  const input = {
    RequestItems: {
      [USER_FARMS_TABLE]: reqs,
    },
  };
  const command = new BatchWriteItemCommand(input);
  await client.send(command);
}

async function updateUser(id, farmIds, updatedAt) {
  const { USERS_TABLE_NAME } = process.env;
  const marshalled = marshall({ farmIds, updatedAt });
  const expressionAttVal = {};
  for (const [key, value] of Object.entries(marshalled)) {
    expressionAttVal[`:${key}`] = value;
  }
  const input = {
    TableName: USERS_TABLE_NAME,
    Key: marshall({ id }),
    UpdateExpression: `SET #F=:farmIds, #U=:updatedAt`,
    ExpressionAttributeNames: {
      "#F": "farmIds",
      "#U": "updatedAt",
    },
    ExpressionAttributeValues: expressionAttVal,
    ReturnValues: "ALL_NEW",
  };
  const command = new UpdateItemCommand(input);
  const res = await client.send(command);
  console.log(res); // --> undefined ???
  const { Attributes } = res;
  return unmarshall(Attributes);
}

module.exports = {
  updateUserFarm: updateUserFarm,
  updateUser: updateUser,
  getFarmsOfUser: getFarmsOfUser,
};

Utils:

function defineToAdd(farmIds, existingFarms) {
  return farmIds.reduce((acc, cv) => {
    const exists = existingFarms.find(({ farmId }) => farmId === cv);
    if (!exists) {
      acc.push(cv);
    }
    return acc;
  }, []);
}

function defineToRemove(existingFarms, farmIds) {
  return existingFarms.reduce((acc, cv) => {
    const { id, farmId } = cv;
    if (!farmIds.includes(farmId)) {
      acc.push(id);
    }

    return acc;
  }, []);
}

module.exports = {
  defineToAdd: defineToAdd,
  defineToRemove: defineToRemove,
};

Cognito commands:

const {
  CognitoIdentityProviderClient,
  AdminGetUserCommand,
} = require("@aws-sdk/client-cognito-identity-provider");
const { AWS_REGION } = process.env;

const client = new CognitoIdentityProviderClient({ region: AWS_REGION });

async function getUser(sub) {
  const { USER_POOL_ID } = process.env;
  const input = {
    UserPoolId: USER_POOL_ID,
    Username: sub,
  };

  const command = new AdminGetUserCommand(input);
  return await client.send(command);
}

async function getUserEmail(id) {
  const { UserAttributes } = await getUser(id);
  const { Value: email } = UserAttributes.find(({ Name }) => Name === "email");
  return email;
}

module.exports = {
  getUserEmail: getUserEmail,
};

Chain behaviours for the same command/payload

It isn't unusual in Jest to chain behaviours of a mock, for instance with several sequential calls to mockImplementationOnce. This useful when you want to reproduce some specific behaviour, such as a call succeeding the first time but failing the second time.

I ran into this problem when I was refactoring my code to use this library. I was able to overcome it by refactoring my code a bit and giving each call/responses unique Ids.

I would consider this lower priority since in most cases you should be able to work around it. Still it might help to simplify test code.

Cannot find name 'queueMicrotask'

My implementation is as below :

import {
  CognitoIdentityProviderClient,
  AdminInitiateAuthCommand
} from '@aws-sdk/client-cognito-identity-provider'
import { mockClient } from 'aws-sdk-client-mock'

import { authorizeUser } from '../src/lambda/common/cognito'

import { authResult } from './test-fixtures'

const cognitoMock = mockClient(CognitoIdentityProviderClient)
cognitoMock.onAnyCommand().resolves({})
cognitoMock.on(AdminInitiateAuthCommand).resolves({
  $metadata: {},
  ChallengeName: 'challenge',
  Session: 'session',
  AuthenticationResult: authResult,
  ChallengeParameters: {}
})

describe('Cognito Service', () => {
  beforeEach(() => {
    cognitoMock.reset()
  })

  it('Auth Response is returned', async () => {
    // arrange
    // act
    const response = await authorizeUser(
      'username',
      'password',
      'userPoolId',
      'userPoolClientId'
    )
    // assert
    expect(response).toBeTruthy()
    expect(response.Session).toBe('session')
  })
})

I receive an error with unrecongnised typeOf queueMicrotask: typeof queueMicrotask

error TS2304: Cannot find name 'queueMicrotask'.
11     queueMicrotask: typeof queueMicrotask;

I also received couple of other errors related to the Mocks.

TS2345: Argument of type 'typeof CognitoIdentityProviderClient' is not assignable to parameter of type 'InstanceOrClassType<Client<ServiceInputTypes, MetadataBearer, any>>'.`

and 
`11 const cognitoMock = mockClient(CognitoIdentityProviderClient)

test/cognito.test.ts:13:16 - error TS2345: Argument of type 'typeof AdminInitiateAuthCommand' is not assignable to parameter of type 'new (input: AdminInitiateAuthCommandInput) => AwsCommand<AdminInitiateAuthCommandInput, MetadataBearer, any, any>'.

Point Lambda to the mocked DynamoDBDocumentClient

Hi!
I'm currently developing unit tests for my lambdas and ran into your interesting package.
I'm testing it on one of my lambdas which is pretty straightforward and looks like this:

//lambda.ts
export const handler: APIGatewayProxyHandlerV2 = async (event: any) => {
  const { a, b, c } = JSON.parse(event.body)
  await getObject(a);
//------------------------------//

//dynamodb-manager.ts
const marshallOptions = {
  // Whether to automatically convert empty strings, blobs, and sets to `null`.
  convertEmptyValues: false, // false, by default.
  // Whether to remove undefined values while marshalling.
  removeUndefinedValues: true, // false, by default.
  // Whether to convert typeof object to map attribute.
  convertClassInstanceToMap: false, // false, by default.
}

const unmarshallOptions = {
  // Whether to return numbers as a string instead of converting them to native JavaScript numbers.
  wrapNumbers: false, // false, by default.
}

const translateConfig = { marshallOptions, unmarshallOptions }

const ddbClient = new DynamoDBClient({})
const ddbDocumentClient = DynamoDBDocumentClient.from(ddbClient, translateConfig)
  export const getObject = async (id) =>{
   return await ddbDocumentClient.send(new GetCommand({TableName: blabla, Key: {id} })
}

And my lambda.test.js looks like this:

const { DynamoDBDocumentClient, GetCommand } = require("@aws-sdk/lib-dynamodb")
const { expect } = require("chai")
const { handler } = require("../../../src/lambda")
const happyEvent = require("../create-technical-assets/fixtures/happy-path/event.json")
const { mockClient } = require("aws-sdk-client-mock")

describe("testing this for the first time", () => {
  beforeEach(() => {
    const ddbMock = mockClient(DynamoDBDocumentClient)
    ddbMock
      .on(GetCommand, {
        TableName: blabla,
        Key: { id: process.env.ID },
      })
      .resolves({
        Items: [
          {
            ...
          },
        ],
      })
  })

  it("should pass", async () => {
    const result = await handler(happyEvent)
    console.log("result", JSON.parse(result.body))
  })
})

However, I feel like my Lambda isn't detecting the mocked database, am I doing something wrong?

Unable to mock "Top-Level" awaits AWS sdk calls

Amazon now recommends using top-level awaits for performance in lambdas.
https://aws.amazon.com/blogs/compute/using-node-js-es-modules-and-top-level-await-in-aws-lambda/

Unfortunately mocks do not work on "top-level awaits.

Example code from the amazon page linked above:

// method2 โ€“ ES module

// ES module import syntax
import { SSMClient, GetParameterCommand } from "@aws-sdk/client-ssm"; 

const ssmClient = new SSMClient();
const input = { "Name": "/configItem" }
const command = new GetParameterCommand(input);
const parameter = await ssmClient.send(command); // top-level await

export async function handler() {
    const response = {
        statusCode: 200,
        "body": parameter.Parameter.Value
    };
    return response;
};

Simple test:

import {mockClient} from 'aws-sdk-client-mock';
import { SSMClient, GetParametersByPathCommand } from '@aws-sdk/client-ssm';

const snsMock = mockClient(SSMClient);
snsMock.resolves({value: "hello"});

import {handler} from '../index.js'

const result = await handler();
console.log(result);

result:
CredentialsProviderError: Could not load credentials from any providers

Thankyou

Hey @m-radzikowski

This isn't an issue. I just wanted to take the time and say thank you for creating this library. It's saved me a ton of time every single time I've worked on a project. Thankyou.

Thankyou

Problem rejecting mocked SQSClient

I'm trying to use rejects function, but always got an error pointing to rejects() without reason.
Does version has to be exact same as 3.18.0? My current version is 3.30.0.
Screen Shot 2564-09-13 at 14 11 03

Can't mock SQSClient

Hi I am getting a weird issue with the mockClient method

It works perfectly for when mocking EventBridgeClient, but when I try to mock SQSClient, it throw error

import { EventBridgeClient, PutEventsCommand } from "@aws-sdk/client-eventbridge";
import { SQSClient, SendMessageCommand } from "@aws-sdk/client-sqs";

const mockedEBClient = mockClient(EventBridgeClient); // <-- this work
const mockSqsClient = mockClient(SQSClient); // <-- this failed 

Error:

error TS2345: Argument of type 'typeof SQSClient' is not assignable to parameter of type 'InstanceOrClassType<Client<ServiceInputTypes, MetadataBearer, any>>'.
      Type 'typeof SQSClient' is not assignable to type 'ClassType<Client<ServiceInputTypes, MetadataBearer, any>>'.
        The types of 'prototype.middlewareStack.concat' are incompatible between these types.
          Type '<InputType extends ServiceInputTypes, OutputType extends ServiceOutputTypes>(from: MiddlewareStack<InputType, OutputType>) => MiddlewareStack<InputType, OutputType>' is not assignable to type '<InputType extends ServiceInputTypes, OutputType extends MetadataBearer>(from: MiddlewareStack<InputType, OutputType>) => MiddlewareStack<InputType, OutputType>'.
            Types of parameters 'from' and 'from' are incompatible.
              Type 'MiddlewareStack<InputType, OutputType>' is not assignable to type 'MiddlewareStack<InputType, ServiceOutputTypes>'.
                Types of property 'addRelativeTo' are incompatible.
                  Type '(middleware: MiddlewareType<InputType, OutputType>, options: RelativeMiddlewareOptions) => void' is not assignable to type '(middleware: MiddlewareType<InputType, ServiceOutputTypes>, options: RelativeMiddlewareOptions) => void'.
                    Types of parameters 'middleware' and 'middleware' are incompatible.
                      Type 'MiddlewareType<InputType, ServiceOutputTypes>' is not assignable to type 'MiddlewareType<InputType, OutputType>'.
                        Type 'InitializeMiddleware<InputType, ServiceOutputTypes>' is not assignable to type 'MiddlewareType<InputType, OutputType>'.
                          Type 'InitializeMiddleware<InputType, ServiceOutputTypes>' is not assignable to type 'InitializeMiddleware<InputType, OutputType>'.
                            Call signature return types 'InitializeHandler<InputType, ServiceOutputTypes>' and 'InitializeHandler<InputType, OutputType>' are incompatible.
                              Type 'Promise<InitializeHandlerOutput<ServiceOutputTypes>>' is not assignable to type 'Promise<InitializeHandlerOutput<OutputType>>'.
                                Type 'InitializeHandlerOutput<ServiceOutputTypes>' is not assignable to type 'InitializeHandlerOutput<OutputType>'.
                                  Types of property 'output' are incompatible.
                                    Type 'ServiceOutputTypes' is not assignable to type 'OutputType'.
                                      'ServiceOutputTypes' is assignable to the constraint of type 'OutputType', but 'OutputType' could be instantiated with a different subtype of constraint 'MetadataBearer'.
                                        Type 'AddPermissionCommandOutput' is not assignable to type 'OutputType'.
                                          'AddPermissionCommandOutput' is assignable to the constraint of type 'OutputType', but 'OutputType' could be instantiated with a different subtype of constraint 'MetadataBearer'.

    17 const mockSqsClient = mockClient(SQSClient);

Types for callsFake()

Mock callsFake() method is not typed. The input argument passed to the mock function should be typed with an intersection of all Client commands inputs, and similar for the output.


Please vote with ๐Ÿ‘ ย  if you need this.

Question about this package

Hey man!
I'm currently a backend developer for a serverless platform on AWS, and we're interacting with a lot of AWS Services (S3, DynamoDB, CodeBuild...etc.). I was wondering whether you have a list of clients the mocking is compatible with or if the mocking is service-agnostic.

Thanks in advance!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.