diff --git a/README.md b/README.md index 1fac63e..ea4bb44 100644 --- a/README.md +++ b/README.md @@ -229,6 +229,18 @@ snsMock .rejects('mocked rejection'); ``` +```typescript +const throttlingError = new Error('mocked rejection'); +throttlingError.name = 'ThrottlingException'; + +snsMock + .rejects(throttlingError); +``` + +In `rejects()`, you can pass a string, an `Error` instance, +or an object with properties. +In each case, it will be converted to an `Error` instance. + Specify custom mock function: ```typescript diff --git a/packages/aws-sdk-client-mock/src/awsClientStub.ts b/packages/aws-sdk-client-mock/src/awsClientStub.ts index c906113..21666a3 100644 --- a/packages/aws-sdk-client-mock/src/awsClientStub.ts +++ b/packages/aws-sdk-client-mock/src/awsClientStub.ts @@ -7,118 +7,22 @@ export type AwsClientBehavior = export interface Behavior { - /** - * Allows specifying the behavior for any Command with given input (parameters). - * - * If the input is not specified, the given behavior will be used for any Command with any input. - * - * Calling `onAnyCommand()` without parameters is not required to specify the default behavior for any Command, - * but can be used for readability. - * - * @example - * ```ts - * clientMock.onAnyCommand().resolves(123) - * ``` - * - * is same as: - * - * ```ts - * clientMock.resolves(123) - * ``` - * - * @param input Command payload to match - * @param strict Should the payload match strictly (default false, will match if all defined payload properties match) - */ onAnyCommand(input?: Partial, strict?: boolean): Behavior; - /** - * Allows specifying the behavior for a given Command type and its input (parameters). - * - * If the input is not specified, it will match any Command of that type. - * - * @param command Command type to match - * @param input Command payload to match - * @param strict Should the payload match strictly (default false, will match if all defined payload properties match) - */ on( command: new (input: TCmdInput) => AwsCommand, input?: Partial, strict?: boolean, ): Behavior; - /** - * Sets a successful response that will be returned from any `Client#send()` invocation. - * - * @param response Content to be returned - */ resolves(response: CommandResponse): AwsStub; - /** - * Sets a successful response that will be returned from one `Client#send()` invocation. - * - * Can be chained so that successive invocations return different responses. When there are no more - * `resolvesOnce()` responses to use, invocations will return a response specified by `resolves()`. - * - * @example - * ```js - * clientMock - * .resolvesOnce('first call') - * .resolvesOnce('second call') - * .resolves('default'); - * ``` - * - * @param response Content to be returned - */ resolvesOnce(response: CommandResponse): Behavior; - /** - * Sets a failure response that will be returned from any `Client#send()` invocation. - * The response will always be an `Error` instance. - * - * @param error Error text, Error instance or Error parameters to be returned - */ rejects(error?: string | Error | AwsError): AwsStub; - /** - * Sets a failure response that will be returned from one `Client#send()` invocation. - * The response will always be an `Error` instance. - * - * Can be chained so that successive invocations return different responses. When there are no more - * `rejectsOnce()` responses to use, invocations will return a response specified by `rejects()`. - * - * @example - * ```js - * clientMock - * .rejectsOnce('first call') - * .rejectsOnce('second call') - * .rejects('default'); - * ``` - * - * @param error Error text, Error instance or Error parameters to be returned - */ rejectsOnce(error?: string | Error | AwsError): Behavior; - /** - * Sets a function that will be called on any `Client#send()` invocation. - * - * @param fn Function taking Command input and returning result - */ callsFake(fn: (input: any, getClient: () => Client) => any): AwsStub; // TODO Types - /** - * Sets a function that will be called on any `Client#send()` invocation. - * - * Can be chained so that successive invocations call different functions. When there are no more - * `callsFakeOnce()` functions to use, invocations will call a function specified by `callsFake()`. - * - * @example - * ```js - * clientMock - * .callsFakeOnce(cmd => 'first call') - * .callsFakeOnce(cmd => 'second call') - * .callsFake(cmd => 'default'); - * ``` - * - * @param fn Function taking Command input and returning result - */ callsFakeOnce(fn: (input: any, getClient: () => Client) => any): Behavior; // TODO Types } @@ -227,11 +131,43 @@ export class AwsStub(input?: Partial, strict = false): CommandBehavior { const cmdStub = this.send.withArgs(this.createInputMatcher(input, strict)); return new CommandBehavior(this, cmdStub); } + /** + * Allows specifying the behavior for a given Command type and its input (parameters). + * + * If the input is not specified, it will match any Command of that type. + * + * @example + * ```js + * snsMock + * .on(PublishCommand, {Message: 'My message'}) + * .resolves({MessageId: '111'}); + * ``` + * + * @param command Command type to match + * @param input Command payload to match + * @param strict Should the payload match strictly (default false, will match if all defined payload properties match) + */ on( command: new (input: TCmdInput) => AwsCommand, input?: Partial, strict = false, ): CommandBehavior { @@ -246,26 +182,134 @@ export class AwsStub): AwsStub { return this.onAnyCommand().resolves(response); } + /** + * Sets a successful response that will be returned from one `Client#send()` invocation. + * + * Can be chained so that successive invocations return different responses. When there are no more + * `resolvesOnce()` responses to use, invocations will return a response specified by `resolves()`. + * + * @example + * ```js + * snsMock + * .resolvesOnce({MessageId: '111'}) // first call + * .resolvesOnce({MessageId: '222'}) // second call + * .resolves({MessageId: '333'}); // default + * ``` + * + * @param response Content to be returned + */ resolvesOnce(response: CommandResponse): CommandBehavior { return this.onAnyCommand().resolvesOnce(response); } + /** + * Sets a failure response that will be returned from any `Client#send()` invocation. + * The response will always be an `Error` instance. + * + * @example + * ```js + * snsMock + * .rejects('mocked rejection'); + *``` + * + * @example + * ```js + * const throttlingError = new Error('mocked rejection'); + * throttlingError.name = 'ThrottlingException'; + * snsMock + * .rejects(throttlingError); + * ``` + * + * @param error Error text, Error instance or Error parameters to be returned + */ rejects(error?: string | Error | AwsError): AwsStub { return this.onAnyCommand().rejects(error); } + /** + * Sets a failure response that will be returned from one `Client#send()` invocation. + * The response will always be an `Error` instance. + * + * Can be chained so that successive invocations return different responses. When there are no more + * `rejectsOnce()` responses to use, invocations will return a response specified by `rejects()`. + * + * @example + * ```js + * snsMock + * .rejectsOnce('first mocked rejection') + * .rejectsOnce('second mocked rejection') + * .rejects('default mocked rejection'); + * ``` + * + * @param error Error text, Error instance or Error parameters to be returned + */ rejectsOnce(error?: string | Error | AwsError): CommandBehavior { return this.onAnyCommand().rejectsOnce(error); } + /** + * Sets a function that will be called on any `Client#send()` invocation. + * + * @example + * ```js + * snsMock + * .callsFake(input => { + * if (input.Message === 'My message') { + * return {MessageId: '111'}; + * } else { + * throw new Error('mocked rejection'); + * } + * }); + * ``` + * + * @example + * Result based on the `Client` configuration: + * ```js + * snsMock + * .callsFake(async (input, getClient) => { + * const client = getClient(); + * const region = await client.config.region(); + * return {MessageId: region.substring(0, 2)}; + * }); + * ``` + * + * @param fn Function taking Command input and returning result + */ callsFake(fn: (input: any, getClient: () => Client) => any): AwsStub { return this.onAnyCommand().callsFake(fn); } + /** + * Sets a function that will be called once, on any `Client#send()` invocation. + * + * Can be chained so that successive invocations call different functions. When there are no more + * `callsFakeOnce()` functions to use, invocations will call a function specified by `callsFake()`. + * + * @example + * ```js + * snsMock + * .callsFakeOnce(cmd => {MessageId: '111'}) // first call + * .callsFakeOnce(cmd => {MessageId: '222'}) // second call + * .callsFake(cmd => {MessageId: '000'}); // default + * ``` + * + * @param fn Function taking Command input and returning result + */ callsFakeOnce(fn: (input: any, getClient: () => Client) => any): CommandBehavior { return this.onAnyCommand().callsFakeOnce(fn); } @@ -292,31 +336,108 @@ export class CommandBehavior(input?: Partial, strict?: boolean): Behavior { return this.clientStub.onAnyCommand(input, strict); } + /** + * @deprecated Using this method means that the previously set `.on(Command)` was not followed by resolves/rejects/callsFake call. + * If this is legitimate behavior, please open an issue with your use case. + */ on( command: new (input: TCmdInput) => AwsCommand, input?: Partial, strict = false, ): CommandBehavior { return this.clientStub.on(command, input, strict); } + /** + * Sets a successful response that will be returned from `Client#send()` invocation for the current `Command`. + * + * @example + * ```js + * snsMock + * .on(PublishCommand) + * .resolves({MessageId: '111'}); + * ``` + * + * @param response Content to be returned + */ resolves(response: CommandResponse): AwsStub { this.send.resolves(response); return this.clientStub; } + /** + * Sets a successful response that will be returned from one `Client#send()` invocation for the current `Command`. + * + * Can be chained so that successive invocations return different responses. When there are no more + * `resolvesOnce()` responses to use, invocations will return a response specified by `resolves()`. + * + * @example + * ```js + * snsMock + * .on(PublishCommand) + * .resolvesOnce({MessageId: '111'}) // first call + * .resolvesOnce({MessageId: '222'}) // second call + * .resolves({MessageId: '333'}); // default + * ``` + * + * @param response Content to be returned + */ resolvesOnce(response: CommandResponse): CommandBehavior { this.send = this.send.onCall(this.nextChainableCallNumber++).resolves(response); return this; } + /** + * Sets a failure response that will be returned from `Client#send()` invocation for the current `Command`. + * The response will always be an `Error` instance. + * + * @example + * ```js + * snsMock + * .on(PublishCommand) + * .rejects('mocked rejection'); + *``` + * + * @example + * ```js + * const throttlingError = new Error('mocked rejection'); + * throttlingError.name = 'ThrottlingException'; + * snsMock + * .on(PublishCommand) + * .rejects(throttlingError); + * ``` + * + * @param error Error text, Error instance or Error parameters to be returned + */ rejects(error?: string | Error | AwsError): AwsStub { this.send.rejects(CommandBehavior.normalizeError(error)); return this.clientStub; } + /** + * Sets a failure response that will be returned from one `Client#send()` invocation for the current `Command`. + * The response will always be an `Error` instance. + * + * Can be chained so that successive invocations return different responses. When there are no more + * `rejectsOnce()` responses to use, invocations will return a response specified by `rejects()`. + * + * @example + * ```js + * snsMock + * .on(PublishCommand) + * .rejectsOnce('first mocked rejection') + * .rejectsOnce('second mocked rejection') + * .rejects('default mocked rejection'); + * ``` + * + * @param error Error text, Error instance or Error parameters to be returned + */ rejectsOnce(error?: string | Error | AwsError): CommandBehavior { this.send.onCall(this.nextChainableCallNumber++).rejects(CommandBehavior.normalizeError(error)); return this; @@ -334,11 +455,58 @@ export class CommandBehavior { + * if (input.Message === 'My message') { + * return {MessageId: '111'}; + * } else { + * throw new Error('mocked rejection'); + * } + * }); + * ``` + * + * @example + * Result based on the `Client` configuration: + * ```js + * snsMock + * .on(PublishCommand) + * .callsFake(async (input, getClient) => { + * const client = getClient(); + * const region = await client.config.region(); + * return {MessageId: region.substring(0, 2)}; + * }); + * ``` + * + * @param fn Function taking Command input and returning result + */ callsFake(fn: (input: any, getClient: () => Client) => any): AwsStub { this.send.callsFake(cmd => this.fakeFnWrapper(cmd, fn)); return this.clientStub; } + /** + * Sets a function that will be called once on `Client#send()` invocation for the current `Command`. + * + * Can be chained so that successive invocations call different functions. When there are no more + * `callsFakeOnce()` functions to use, invocations will call a function specified by `callsFake()`. + * + * @example + * ```js + * snsMock + * .on(PublishCommand) + * .callsFakeOnce(cmd => {MessageId: '111'}) // first call + * .callsFakeOnce(cmd => {MessageId: '222'}) // second call + * .callsFake(cmd => {MessageId: '000'}); // default + * ``` + * + * @param fn Function taking Command input and returning result + */ callsFakeOnce(fn: (input: any, getClient: () => Client) => any): CommandBehavior { this.send.onCall(this.nextChainableCallNumber++).callsFake(cmd => this.fakeFnWrapper(cmd, fn)); return this;