import { match } from 'sinon'; import { mockClient } from './mockClient'; /** * Wrapper on the mocked `Client#send()` method, * allowing to configure its behavior. * * Without any configuration, `Client#send()` invocation returns `undefined`. * * To define resulting variable type easily, use {@link AwsClientStub}. */ var AwsStub = /** @class */ (function () { function AwsStub(client, send) { this.client = client; this.send = send; } /** Returns the class name of the underlying mocked client class */ AwsStub.prototype.clientName = function () { return this.client.constructor.name; }; /** * Resets stub. It will replace the stub with a new one, with clean history and behavior. */ AwsStub.prototype.reset = function () { /* sinon.stub.reset() does not remove the fakes which in some conditions can break subsequent stubs, * so instead of calling send.reset(), we recreate the stub. * See: https://github.com/sinonjs/sinon/issues/1572 * We are only affected by the broken reset() behavior of this bug, since we always use matchers. */ var newStub = mockClient(this.client); this.send = newStub.send; return this; }; /** Resets stub's calls history. */ AwsStub.prototype.resetHistory = function () { this.send.resetHistory(); return this; }; /** Replaces stub with original `Client#send()` method. */ AwsStub.prototype.restore = function () { this.send.restore(); }; /** * Returns recorded calls to the stub. * Clear history with {@link resetHistory} or {@link reset}. */ AwsStub.prototype.calls = function () { return this.send.getCalls(); }; /** * Returns n-th recorded call to the stub. */ AwsStub.prototype.call = function (n) { return this.send.getCall(n); }; /** * Returns recorded calls of given Command only. * @param commandType 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) */ AwsStub.prototype.commandCalls = function (commandType, input, strict) { var _this = this; return this.send.getCalls() .filter(function (call) { var isProperType = call.args[0] instanceof commandType; var inputMatches = _this.createInputMatcher(input, strict).test(call.args[0]); return isProperType && inputMatches; }); }; /** * 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 * ```js * clientMock.onAnyCommand().resolves({}) * ``` * * @param input Command payload to match * @param strict Should the payload match strictly (default false, will match if all defined payload properties match) */ AwsStub.prototype.onAnyCommand = function (input, strict) { if (strict === void 0) { strict = false; } var 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) */ AwsStub.prototype.on = function (command, input, strict) { if (strict === void 0) { strict = false; } var matcher = match.instanceOf(command).and(this.createInputMatcher(input, strict)); var cmdStub = this.send.withArgs(matcher); return new CommandBehavior(this, cmdStub); }; AwsStub.prototype.createInputMatcher = function (input, strict) { if (strict === void 0) { strict = false; } return input !== undefined ? match.has('input', strict ? input : match(input)) : match.any; }; /** * Sets a successful response that will be returned from any `Client#send()` invocation. * * @example * ```js * snsMock * .resolves({MessageId: '111'}); * ``` * * @param response Content to be returned */ AwsStub.prototype.resolves = function (response) { 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 */ AwsStub.prototype.resolvesOnce = function (response) { 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 */ AwsStub.prototype.rejects = function (error) { 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 */ AwsStub.prototype.rejectsOnce = function (error) { 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 */ AwsStub.prototype.callsFake = function (fn) { 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 */ AwsStub.prototype.callsFakeOnce = function (fn) { return this.onAnyCommand().callsFakeOnce(fn); }; return AwsStub; }()); export { AwsStub }; var CommandBehavior = /** @class */ (function () { function CommandBehavior(clientStub, send) { var _this = this; this.clientStub = clientStub; this.send = send; /** * Counter to simulate chainable `resolvesOnce()` and similar `*Once()` methods with Sinon `Stub#onCall()`. * The counter is increased with every `*Once()` method call. */ this.nextChainableCallNumber = 0; /** * Function to get the current Client object from inside the `callsFake()` callback. * Since this is called from the callback when the mock function is executed, * the current Client is the last on the Sinon `Stub#thisValues` list. */ this.getClient = function () { return _this.send.thisValues[_this.send.thisValues.length - 1]; }; } /** * @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. */ CommandBehavior.prototype.onAnyCommand = function (input, strict) { 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. */ CommandBehavior.prototype.on = function (command, input, strict) { if (strict === void 0) { strict = false; } 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 */ CommandBehavior.prototype.resolves = function (response) { 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 */ CommandBehavior.prototype.resolvesOnce = function (response) { 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 */ CommandBehavior.prototype.rejects = function (error) { 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 */ CommandBehavior.prototype.rejectsOnce = function (error) { this.send.onCall(this.nextChainableCallNumber++).rejects(CommandBehavior.normalizeError(error)); return this; }; CommandBehavior.normalizeError = function (error) { if (typeof error === 'string') { return new Error(error); } if (!(error instanceof Error)) { return Object.assign(new Error(), error); } return error; }; /** * Sets a function that will be called on `Client#send()` invocation for the current `Command`. * * @example * ```js * snsMock * .on(PublishCommand) * .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 * .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 */ CommandBehavior.prototype.callsFake = function (fn) { var _this = this; this.send.callsFake(function (cmd) { return _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 */ CommandBehavior.prototype.callsFakeOnce = function (fn) { var _this = this; this.send.onCall(this.nextChainableCallNumber++).callsFake(function (cmd) { return _this.fakeFnWrapper(cmd, fn); }); return this; }; CommandBehavior.prototype.fakeFnWrapper = function (cmd, fn) { try { // eslint-disable-next-line @typescript-eslint/no-unsafe-return return fn(cmd.input, this.getClient); } catch (err) { return Promise.reject(CommandBehavior.normalizeError(err)); } }; return CommandBehavior; }()); export { CommandBehavior }; //# sourceMappingURL=awsClientStub.js.map