Skip to content

Commit 020735a

Browse files
feat: access Client configuration in callsFake (#164)
* feat: access Client configuration in callsFake * fix: fixed updated client mock type in jest matchers
1 parent 90862cb commit 020735a

File tree

4 files changed

+184
-52
lines changed

4 files changed

+184
-52
lines changed

packages/aws-sdk-client-mock-jest/src/jestMatchers.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,16 @@ interface AwsSdkJestMockBaseMatchers<R> extends Record<string, any> {
5757
/**
5858
* Asserts {@link AwsStub Aws Client Mock} received a {@link command} as defined specific {@link call}
5959
* number with matchin {@link input}
60-
*
60+
*
6161
* @param call call number to assert
6262
* @param command aws-sdk command constructor
63-
* @param input
63+
* @param input
6464
*/
6565
toHaveReceivedNthSpecificCommandWith<TCmdInput extends object, TCmdOutput extends MetadataBearer>(
6666
call: number,
6767
command: new (input: TCmdInput) => AwsCommand<TCmdInput, TCmdOutput>,
68-
input: Partial<TCmdInput>
69-
): R;
68+
input: Partial<TCmdInput>,
69+
): R;
7070
}
7171

7272
interface AwsSdkJestMockAliasMatchers<R> {
@@ -126,16 +126,16 @@ interface AwsSdkJestMockAliasMatchers<R> {
126126
/**
127127
* Asserts {@link AwsStub Aws Client Mock} received a {@link command} as defined specific {@link call}
128128
* number with matchin {@link input}
129-
*
129+
*
130130
* @param call call number to assert
131131
* @param command aws-sdk command constructor
132-
* @param input
132+
* @param input
133133
*/
134134
toReceiveNthSpecificCommandWith<TCmdInput extends object, TCmdOutput extends MetadataBearer>(
135135
call: number,
136136
command: new (input: TCmdInput) => AwsCommand<TCmdInput, TCmdOutput>,
137-
input: Partial<TCmdInput>
138-
): R;
137+
input: Partial<TCmdInput>,
138+
): R;
139139
}
140140

141141
/**
@@ -172,7 +172,7 @@ declare global {
172172
}
173173
}
174174

175-
type ClientMock = AwsStub<any, any>;
175+
type ClientMock = AwsStub<any, any, any>;
176176
type AnyCommand = AwsCommand<any, any>;
177177
type AnySpyCall = SinonSpyCall<[AnyCommand]>;
178178
type MessageFunctionParams<CheckData> = {

packages/aws-sdk-client-mock/src/awsClientStub.ts

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import {Client, Command, MetadataBearer} from '@aws-sdk/types';
22
import {match, SinonSpyCall, SinonStub} from 'sinon';
33
import {mockClient} from './mockClient';
44

5-
export type AwsClientBehavior<TClient extends Client<any, any, any>> =
6-
TClient extends Client<infer TInput, infer TOutput, any> ? Behavior<TInput, TOutput, TOutput> : never;
5+
export type AwsClientBehavior<TClient> =
6+
TClient extends Client<infer TInput, infer TOutput, infer TConfiguration> ? Behavior<TInput, TOutput, TOutput, TConfiguration> : never;
77

8-
export interface Behavior<TInput extends object, TOutput extends MetadataBearer, TCommandOutput extends TOutput> {
8+
export interface Behavior<TInput extends object, TOutput extends MetadataBearer, TCommandOutput extends TOutput, TConfiguration> {
99

1010
/**
1111
* Allows specifying the behavior for any Command with given input (parameters).
@@ -29,7 +29,7 @@ export interface Behavior<TInput extends object, TOutput extends MetadataBearer,
2929
* @param input Command payload to match
3030
* @param strict Should the payload match strictly (default false, will match if all defined payload properties match)
3131
*/
32-
onAnyCommand<TCmdInput extends TInput>(input?: Partial<TCmdInput>, strict?: boolean): Behavior<TInput, TOutput, TOutput>;
32+
onAnyCommand<TCmdInput extends TInput>(input?: Partial<TCmdInput>, strict?: boolean): Behavior<TInput, TOutput, TOutput, TConfiguration>;
3333

3434
/**
3535
* Allows specifying the behavior for a given Command type and its input (parameters).
@@ -42,14 +42,14 @@ export interface Behavior<TInput extends object, TOutput extends MetadataBearer,
4242
*/
4343
on<TCmdInput extends TInput, TCmdOutput extends TOutput>(
4444
command: new (input: TCmdInput) => AwsCommand<TCmdInput, TCmdOutput>, input?: Partial<TCmdInput>, strict?: boolean,
45-
): Behavior<TInput, TOutput, TCmdOutput>;
45+
): Behavior<TInput, TOutput, TCmdOutput, TConfiguration>;
4646

4747
/**
4848
* Sets a successful response that will be returned from any `Client#send()` invocation.
4949
*
5050
* @param response Content to be returned
5151
*/
52-
resolves(response: CommandResponse<TCommandOutput>): AwsStub<TInput, TOutput>;
52+
resolves(response: CommandResponse<TCommandOutput>): AwsStub<TInput, TOutput, TConfiguration>;
5353

5454
/**
5555
* Sets a successful response that will be returned from one `Client#send()` invocation.
@@ -67,15 +67,15 @@ export interface Behavior<TInput extends object, TOutput extends MetadataBearer,
6767
*
6868
* @param response Content to be returned
6969
*/
70-
resolvesOnce(response: CommandResponse<TCommandOutput>): Behavior<TInput, TOutput, TCommandOutput>;
70+
resolvesOnce(response: CommandResponse<TCommandOutput>): Behavior<TInput, TOutput, TCommandOutput, TConfiguration>;
7171

7272
/**
7373
* Sets a failure response that will be returned from any `Client#send()` invocation.
7474
* The response will always be an `Error` instance.
7575
*
7676
* @param error Error text, Error instance or Error parameters to be returned
7777
*/
78-
rejects(error?: string | Error | AwsError): AwsStub<TInput, TOutput>;
78+
rejects(error?: string | Error | AwsError): AwsStub<TInput, TOutput, TConfiguration>;
7979

8080
/**
8181
* Sets a failure response that will be returned from one `Client#send()` invocation.
@@ -94,14 +94,14 @@ export interface Behavior<TInput extends object, TOutput extends MetadataBearer,
9494
*
9595
* @param error Error text, Error instance or Error parameters to be returned
9696
*/
97-
rejectsOnce(error?: string | Error | AwsError): Behavior<TInput, TOutput, TCommandOutput>;
97+
rejectsOnce(error?: string | Error | AwsError): Behavior<TInput, TOutput, TCommandOutput, TConfiguration>;
9898

9999
/**
100100
* Sets a function that will be called on any `Client#send()` invocation.
101101
*
102102
* @param fn Function taking Command input and returning result
103103
*/
104-
callsFake(fn: (input: any) => any): AwsStub<TInput, TOutput>; // TODO Types
104+
callsFake(fn: (input: any, getClient: () => Client<TInput, TOutput, TConfiguration>) => any): AwsStub<TInput, TOutput, TConfiguration>; // TODO Types
105105

106106
/**
107107
* Sets a function that will be called on any `Client#send()` invocation.
@@ -119,7 +119,7 @@ export interface Behavior<TInput extends object, TOutput extends MetadataBearer,
119119
*
120120
* @param fn Function taking Command input and returning result
121121
*/
122-
callsFakeOnce(fn: (input: any) => any): Behavior<TInput, TOutput, TCommandOutput>; // TODO Types
122+
callsFakeOnce(fn: (input: any, getClient: () => Client<TInput, TOutput, TConfiguration>) => any): Behavior<TInput, TOutput, TCommandOutput, TConfiguration>; // TODO Types
123123

124124
}
125125

@@ -133,8 +133,8 @@ export interface Behavior<TInput extends object, TOutput extends MetadataBearer,
133133
* snsMock = mockClient(SNSClient);
134134
* ```
135135
*/
136-
export type AwsClientStub<TClient extends Client<any, any, any>> =
137-
TClient extends Client<infer TInput, infer TOutput, any> ? AwsStub<TInput, TOutput> : never;
136+
export type AwsClientStub<TClient> =
137+
TClient extends Client<infer TInput, infer TOutput, infer TConfiguration> ? AwsStub<TInput, TOutput, TConfiguration> : never;
138138

139139
/**
140140
* Wrapper on the mocked `Client#send()` method,
@@ -144,7 +144,7 @@ export type AwsClientStub<TClient extends Client<any, any, any>> =
144144
*
145145
* To define resulting variable type easily, use {@link AwsClientStub}.
146146
*/
147-
export class AwsStub<TInput extends object, TOutput extends MetadataBearer> implements Behavior<TInput, TOutput, TOutput> {
147+
export class AwsStub<TInput extends object, TOutput extends MetadataBearer, TConfiguration> implements Behavior<TInput, TOutput, TOutput, TConfiguration> {
148148

149149
/**
150150
* Underlying `Client#send()` method Sinon stub.
@@ -154,7 +154,7 @@ export class AwsStub<TInput extends object, TOutput extends MetadataBearer> impl
154154
public send: SinonStub<[AwsCommand<TInput, TOutput>], Promise<TOutput>>;
155155

156156
constructor(
157-
private client: Client<TInput, TOutput, any>,
157+
private client: Client<TInput, TOutput, TConfiguration>,
158158
send: SinonStub<[AwsCommand<TInput, TOutput>], Promise<TOutput>>,
159159
) {
160160
this.send = send;
@@ -168,7 +168,7 @@ export class AwsStub<TInput extends object, TOutput extends MetadataBearer> impl
168168
/**
169169
* Resets stub. It will replace the stub with a new one, with clean history and behavior.
170170
*/
171-
reset(): AwsStub<TInput, TOutput> {
171+
reset(): AwsStub<TInput, TOutput, TConfiguration> {
172172
/* sinon.stub.reset() does not remove the fakes which in some conditions can break subsequent stubs,
173173
* so instead of calling send.reset(), we recreate the stub.
174174
* See: https://github.com/sinonjs/sinon/issues/1572
@@ -180,7 +180,7 @@ export class AwsStub<TInput extends object, TOutput extends MetadataBearer> impl
180180
}
181181

182182
/** Resets stub's calls history. */
183-
resetHistory(): AwsStub<TInput, TOutput> {
183+
resetHistory(): AwsStub<TInput, TOutput, TConfiguration> {
184184
this.send.resetHistory();
185185
return this;
186186
}
@@ -214,7 +214,7 @@ export class AwsStub<TInput extends object, TOutput extends MetadataBearer> impl
214214
commandCalls<TCmd extends AwsCommand<any, any>,
215215
TCmdInput extends TCmd extends AwsCommand<infer TIn, any> ? TIn : never,
216216
TCmdOutput extends TCmd extends AwsCommand<any, infer TOut> ? TOut : never,
217-
>(
217+
>(
218218
commandType: new (input: TCmdInput) => TCmd,
219219
input?: Partial<TCmdInput>,
220220
strict?: boolean,
@@ -227,17 +227,17 @@ export class AwsStub<TInput extends object, TOutput extends MetadataBearer> impl
227227
});
228228
}
229229

230-
onAnyCommand<TCmdInput extends TInput>(input?: Partial<TCmdInput>, strict = false): CommandBehavior<TInput, TOutput, TOutput> {
230+
onAnyCommand<TCmdInput extends TInput>(input?: Partial<TCmdInput>, strict = false): CommandBehavior<TInput, TOutput, TOutput, TConfiguration> {
231231
const cmdStub = this.send.withArgs(this.createInputMatcher(input, strict));
232232
return new CommandBehavior(this, cmdStub);
233233
}
234234

235235
on<TCmdInput extends TInput, TCmdOutput extends TOutput>(
236236
command: new (input: TCmdInput) => AwsCommand<TCmdInput, TCmdOutput>, input?: Partial<TCmdInput>, strict = false,
237-
): CommandBehavior<TInput, TOutput, TCmdOutput> {
237+
): CommandBehavior<TInput, TOutput, TCmdOutput, TConfiguration> {
238238
const matcher = match.instanceOf(command).and(this.createInputMatcher(input, strict));
239239
const cmdStub = this.send.withArgs(matcher);
240-
return new CommandBehavior<TInput, TOutput, TCmdOutput>(this, cmdStub);
240+
return new CommandBehavior<TInput, TOutput, TCmdOutput, TConfiguration>(this, cmdStub);
241241
}
242242

243243
private createInputMatcher<TCmdInput extends TInput>(input?: Partial<TCmdInput>, strict = false) {
@@ -246,71 +246,78 @@ export class AwsStub<TInput extends object, TOutput extends MetadataBearer> impl
246246
: match.any;
247247
}
248248

249-
resolves(response: CommandResponse<TOutput>): AwsStub<TInput, TOutput> {
249+
resolves(response: CommandResponse<TOutput>): AwsStub<TInput, TOutput, TConfiguration> {
250250
return this.onAnyCommand().resolves(response);
251251
}
252252

253-
resolvesOnce(response: CommandResponse<TOutput>): CommandBehavior<TInput, TOutput, TOutput> {
253+
resolvesOnce(response: CommandResponse<TOutput>): CommandBehavior<TInput, TOutput, TOutput, TConfiguration> {
254254
return this.onAnyCommand().resolvesOnce(response);
255255
}
256256

257-
rejects(error?: string | Error | AwsError): AwsStub<TInput, TOutput> {
257+
rejects(error?: string | Error | AwsError): AwsStub<TInput, TOutput, TConfiguration> {
258258
return this.onAnyCommand().rejects(error);
259259
}
260260

261-
rejectsOnce(error?: string | Error | AwsError): CommandBehavior<TInput, TOutput, TOutput> {
261+
rejectsOnce(error?: string | Error | AwsError): CommandBehavior<TInput, TOutput, TOutput, TConfiguration> {
262262
return this.onAnyCommand().rejectsOnce(error);
263263
}
264264

265-
callsFake(fn: (input: any) => any): AwsStub<TInput, TOutput> {
265+
callsFake(fn: (input: any, getClient: () => Client<TInput, TOutput, TConfiguration>) => any): AwsStub<TInput, TOutput, TConfiguration> {
266266
return this.onAnyCommand().callsFake(fn);
267267
}
268268

269-
callsFakeOnce(fn: (input: any) => any): CommandBehavior<TInput, TOutput, TOutput> {
269+
callsFakeOnce(fn: (input: any, getClient: () => Client<TInput, TOutput, TConfiguration>) => any): CommandBehavior<TInput, TOutput, TOutput, TConfiguration> {
270270
return this.onAnyCommand().callsFakeOnce(fn);
271271
}
272272
}
273273

274-
export class CommandBehavior<TInput extends object, TOutput extends MetadataBearer, TCommandOutput extends TOutput> implements Behavior<TInput, TOutput, TCommandOutput> {
274+
export class CommandBehavior<TInput extends object, TOutput extends MetadataBearer, TCommandOutput extends TOutput, TConfiguration> implements Behavior<TInput, TOutput, TCommandOutput, TConfiguration> {
275275

276276
/**
277277
* Counter to simulate chainable `resolvesOnce()` and similar `*Once()` methods with Sinon `Stub#onCall()`.
278278
* The counter is increased with every `*Once()` method call.
279279
*/
280280
private nextChainableCallNumber = 0;
281281

282+
/**
283+
* Function to get the current Client object from inside the `callsFake()` callback.
284+
* Since this is called from the callback when the mock function is executed,
285+
* the current Client is the last on the Sinon `Stub#thisValues` list.
286+
*/
287+
private getClient = () => this.send.thisValues[this.send.thisValues.length - 1] as Client<TInput, TOutput, TConfiguration>;
288+
282289
constructor(
283-
private clientStub: AwsStub<TInput, TOutput>,
290+
private clientStub: AwsStub<TInput, TOutput, TConfiguration>,
284291
private send: SinonStub<[AwsCommand<TInput, TOutput>], unknown>,
285292
) {
286293
}
287294

288-
onAnyCommand<TCmdInput extends TInput>(input?: Partial<TCmdInput>, strict?: boolean): Behavior<TInput, TOutput, TOutput> {
295+
onAnyCommand<TCmdInput extends TInput>(input?: Partial<TCmdInput>, strict?: boolean): Behavior<TInput, TOutput, TOutput, TConfiguration> {
289296
return this.clientStub.onAnyCommand(input, strict);
290297
}
291298

292299
on<TCmdInput extends TInput, TCmdOutput extends TOutput>(
293300
command: new (input: TCmdInput) => AwsCommand<TCmdInput, TCmdOutput>, input?: Partial<TCmdInput>, strict = false,
294-
): CommandBehavior<TInput, TOutput, TCmdOutput> {
301+
): CommandBehavior<TInput, TOutput, TCmdOutput, TConfiguration> {
295302
return this.clientStub.on(command, input, strict);
296303
}
297304

298-
resolves(response: CommandResponse<TCommandOutput>): AwsStub<TInput, TOutput> {
305+
resolves(response: CommandResponse<TCommandOutput>): AwsStub<TInput, TOutput, TConfiguration> {
299306
this.send.resolves(response);
300307
return this.clientStub;
301308
}
302309

303-
resolvesOnce(response: CommandResponse<TCommandOutput>): CommandBehavior<TInput, TOutput, TCommandOutput> {
310+
resolvesOnce(response: CommandResponse<TCommandOutput>): CommandBehavior<TInput, TOutput, TCommandOutput, TConfiguration> {
304311
this.send = this.send.onCall(this.nextChainableCallNumber++).resolves(response);
305312
return this;
306313
}
307314

308-
rejects(error?: string | Error | AwsError): AwsStub<TInput, TOutput> {
315+
rejects(error?: string | Error | AwsError): AwsStub<TInput, TOutput, TConfiguration> {
309316
this.send.rejects(CommandBehavior.normalizeError(error));
310317
return this.clientStub;
311318
}
312319

313-
rejectsOnce(error?: string | Error | AwsError): CommandBehavior<TInput, TOutput, TCommandOutput> {
320+
rejectsOnce(error?: string | Error | AwsError): CommandBehavior<TInput, TOutput, TCommandOutput, TConfiguration> {
314321
this.send.onCall(this.nextChainableCallNumber++).rejects(CommandBehavior.normalizeError(error));
315322
return this;
316323
}
@@ -327,13 +334,13 @@ export class CommandBehavior<TInput extends object, TOutput extends MetadataBear
327334
return error;
328335
}
329336

330-
callsFake(fn: (input: any) => any): AwsStub<TInput, TOutput> {
331-
this.send.callsFake(cmd => fn(cmd.input));
337+
callsFake(fn: (input: any, getClient: () => Client<TInput, TOutput, TConfiguration>) => any): AwsStub<TInput, TOutput, TConfiguration> {
338+
this.send.callsFake(cmd => fn(cmd.input, this.getClient));
332339
return this.clientStub;
333340
}
334341

335-
callsFakeOnce(fn: (input: any) => any): CommandBehavior<TInput, TOutput, TCommandOutput> {
336-
this.send.onCall(this.nextChainableCallNumber++).callsFake(cmd => fn(cmd.input));
342+
callsFakeOnce(fn: (input: any, getClient: () => Client<TInput, TOutput, TConfiguration>) => any): CommandBehavior<TInput, TOutput, TCommandOutput, TConfiguration> {
343+
this.send.onCall(this.nextChainableCallNumber++).callsFake(cmd => fn(cmd.input, this.getClient));
337344
return this;
338345
}
339346
}

packages/aws-sdk-client-mock/src/mockClient.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import {AwsClientStub, AwsStub} from './awsClientStub';
99
* @param client `Client` type or instance to replace the method
1010
* @return Stub allowing to configure Client's behavior
1111
*/
12-
export const mockClient = <TInput extends object, TOutput extends MetadataBearer>(
13-
client: InstanceOrClassType<Client<TInput, TOutput, any>>,
14-
): AwsClientStub<Client<TInput, TOutput, any>> => {
12+
export const mockClient = <TInput extends object, TOutput extends MetadataBearer, TConfiguration>(
13+
client: InstanceOrClassType<Client<TInput, TOutput, TConfiguration>>,
14+
): AwsClientStub<Client<TInput, TOutput, TConfiguration>> => {
1515
const instance = isClientInstance(client) ? client : client.prototype;
1616

1717
const send = instance.send;
@@ -22,7 +22,7 @@ export const mockClient = <TInput extends object, TOutput extends MetadataBearer
2222
const sendStub = stub(instance, 'send') as SinonStub<[Command<TInput, any, TOutput, any, any>], Promise<TOutput>>;
2323

2424
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
25-
return new AwsStub<TInput, TOutput>(instance, sendStub);
25+
return new AwsStub<TInput, TOutput, TConfiguration>(instance, sendStub);
2626
};
2727

2828
type ClassType<T> = {

0 commit comments

Comments
 (0)