m-radzikowski / aws-sdk-client-mock

AWS JavaScript SDK v3 mocks for easy unit testing. 🖋️ Typed 🔬 Tested 📄 Documented 🛠️ Maintained
https://m-radzikowski.github.io/aws-sdk-client-mock/
MIT License
790 stars 38 forks source link

S3Client `HeadObjectCommand` doesn't work with new error handling #189

Closed garysassano closed 9 months ago

garysassano commented 11 months ago

Checklist

Bug description

While error classes function as expected for most commands, they seem to behave differently for the HeadObjectCommand.

For example, I have this method:

/**
 * Check if an object exists in the bucket
 *
 * @param key Key of the object
 */
public async exists(key: string): Promise<boolean> {
  const command = new HeadObjectCommand({
    Bucket: this.bucketName,
    Key: key,
  });

  try {
    await this.s3Client.send(command);
    return true;
  } catch (error) {
    // SDK v2 approach
    if ((error as Error).name === "NotFound") {
      console.log("NotFound@v2");
    }
    // SDK v3 approach
    if (error instanceof NotFound) {
      console.log("NotFound@v3");
    }
    throw error;
  }
}

During my tests using the method above, the only console output I received was NotFound@v2, suggesting that there might be an issue.

Reproduction

Environment

stern-shawn commented 9 months ago

Hi! This came up while I was looking to mock this same operation in my own project.

if (error instanceof NotFound) {
  console.log("NotFound@v3");
}

I'm able to test this reliably with something like this:

const s3Client = mockClient(S3Client);
// Inside of an individual test case
s3Client.on(HeadObjectCommand).rejects(new NotFound({ message: 'sorry', $metadata: {} }));
const res = await exists(...inputs);
expect(res).toEqual(false)

My libraries are at these versions:

Maybe see if upgrading the mock library to v3 fixes it for you?

garysassano commented 9 months ago

@stern-shawn The bug is present also in aws-sdk-client-mock@3.0.0

m-radzikowski commented 9 months ago

Like @stern-shawn wrote, if you provide a custom error object to the rejects() method, you will get it thrown back, so this works without an issue:

import {HeadObjectCommand, ListObjectsV2Command, NotFound, S3Client} from "@aws-sdk/client-s3";
import {mockClient} from "aws-sdk-client-mock";

const s3 = new S3Client({});
const s3Mock = mockClient(S3Client);

it('rejects with custom error', async () => {
    s3Mock.on(HeadObjectCommand).rejects(new NotFound({message: 'sorry', $metadata: {}}));

    const headObjectCommand = new HeadObjectCommand({Bucket: 'aa', Key: 'bb'});
    await expect(s3.send(headObjectCommand)).rejects.toThrow(NotFound);
});

If you provide just the message like s3Mock.on(HeadObjectCommand).rejects('my message'), you will get a standard Error object back with the message included.

Please provide a reproduction example if something does not work for you.