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

Breaking Change with AwsStub v 2.2.0 through the introduction of the TConfiguration type #167

Closed nikimicallef closed 1 year ago

nikimicallef commented 1 year ago

Checklist

Bug description

V2.2.0 changes the required types for the AwsStub through the introduction of the TConfiguration type. However, it is not clear to me what I need to set there. The only part I found in the documentation regarding this parameter is the following

Without any configuration, Client#send() invocation returns undefined.

Can we get a bit more information of what is meant to be set for this type parameter please?

Reproduction

s3Client: AwsStub<ServiceInputTypes, ServiceOutputTypes>,

Environment

m-radzikowski commented 1 year ago

Hey, sorry for that. AwsStub, while publicly exposed, was not intended to be used directly. Instead, you should use AwsClientStub type:

let snsMock: AwsClientStub<SNSClient>;
snsMock = mockClient(SNSClient);

Although I know that's not mentioned in the README. I will improve the docs regarding this.

Please let me know if that solves your issue.

nikimicallef commented 1 year ago

I've tried a few more things but we still have the same issue. Maybe I can provide more context.

We have something similar to the following

import {AwsStub, mockClient} from 'aws-sdk-client-mock';
import {S3Client} from '@aws-sdk/client-s3';

function fun(s3Client: AwsStub<ServiceInputTypes, ServiceOutputTypes>) { ... }
const s3Client = mockClient(S3Client);
fun(s3Client);

This obviously breaks since the AwsStub now requires a 3rd parameter. Based on your feedback, I tried the following.

import {AwsClientStub, mockClient} from 'aws-sdk-client-mock';
import {S3Client} from '@aws-sdk/client-s3';

function fun(s3Client: AwsClientStub<S3Client>) { ... }
const s3Client = mockClient(S3Client);
fun(s3Client);

This returns the following on runtime

Argument of type 'AwsStub<ServiceInputTypes, ServiceOutputTypes, SmithyResolvedConfiguration<HttpHandlerOptions>>' is not assignable to parameter of type 'AwsStub<ServiceInputTypes, ServiceOutputTypes, S3ClientResolvedConfig>'.
      Type 'SmithyResolvedConfiguration<HttpHandlerOptions>' is not assignable to type 'S3ClientResolvedConfig'.
        Type 'SmithyConfiguration<HttpHandlerOptions>' is missing the following properties from type 'Required<ClientDefaults>': sha256, urlParser, bodyLengthChecker, streamCollector, and 24 more.

    28         fun(s3Client, expectedRetentionUntil);

This seems to be because the type returned by the mockClient is AwsStub<ServiceInputTypes, ServiceOutputTypes, S3ClientResolvedConfig> while the AwsClientStub<S3Client> is of type AwsStub<ServiceInputTypes, ServiceOutputTypes, SmithyResolvedConfiguration<HttpHandlerOptions>>. I'm not sure if it is also the case for the SNS Client, as per your example, but we are using both the S3 Client and the Dynamo DB Client and both have the same issue.

A temporary solution is to leave my original code and set the 3rd parameter to any but that is not a 'real' solution imo.

m-radzikowski commented 1 year ago

Just to let you know I'm looking into that:

  1. Yes, it should work with AwsClientStub<S3Client> like you posted in the example.
  2. But it throws type error if tsconfig options are set with strict=true or strictFunctionTypes=true. Weirdly, if you change strictFunctionTypes to false (or strict=false, which implies the other), error disappears. I would expect the opposite.
  3. With strictFunctionTypes=true the mockClient() output type is AwsStub<ServiceInputTypes, ServiceOutputTypes, S3ClientResolvedConfig> and it conforms to the AwsClientStub<S3Client> type.
  4. With strictFunctionTypes=false the output type is AwsStub<ServiceInputTypes, ServiceOutputTypes, SmithyResolvedConfiguration<HttpHandlerOptions>> - seems like TS makes few jumps and resolves to a different type

Ideally, the mockClient() should return AwsClientStub<> type like it's declared, but it does not. Instead, it hops again and returns AwsStub directly. This makes types in library unit tests slightly different from when library is built, which originally made me unaware of this and let this problem pass tests.

While I agree this is an unexpected breaking change, releasing a patch with a rollback would cause more chaos. Especially that AwsStub type is probably not used in most cases.

The workaround, for now, is to use AwsStub<ServiceInputTypes, ServiceOutputTypes, any> or, if you have strict=true or strictFunctionTypes=true in tsconfig, AwsClientStub<S3Client>. Meanwhile, I will try to fix the types.

m-radzikowski commented 1 year ago

After spending another evening on this, my recommendation is to:

I can't get rid of type errors between AwsClientStub type and AwsStub class with strictFunctionTypes=false.

I'm leaving this open, but I don't plan anything better soon simply because I have no further ideas at the moment.

nikimicallef commented 1 year ago

Thank you for your effort on this issue. I think leaving it open with the suggested solution is a decent enough 'conclusion'. Thank you :)

ArmenOzcelik commented 1 year ago

Hi,

I had the same issue when upgrading to "@aws-sdk/client-s3": "3.369.0" and "aws-sdk-client-mock": "3.0.0". As you suggested, adding strictFunctionTypes: true in tsconfig file resolved the issue without breaking something else.

This thread saved me a lot of time, thanks to both of you!