aws-sdk-vitest-mock 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +265 -0
- package/index.cjs +1 -0
- package/index.d.ts +2 -0
- package/index.js +113 -0
- package/lib/matchers.d.ts +29 -0
- package/lib/mock-client.d.ts +48 -0
- package/lib/vitest-setup.d.ts +1 -0
- package/matchers-D1XEjFLq.cjs +1 -0
- package/matchers-DclpcT4f.js +33 -0
- package/package.json +74 -0
- package/vitest-setup.cjs +1 -0
- package/vitest-setup.js +3 -0
package/README.md
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="logo.png" alt="aws-sdk-vitest-mock logo" width="180" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">AWS SDK Vitest Mock</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
A powerful, type-safe mocking library for AWS SDK v3 with Vitest
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## ✨ Features
|
|
14
|
+
|
|
15
|
+
- 🎯 **Type-Safe Mocking** - Full TypeScript support with strict type checking
|
|
16
|
+
- 📦 **Zero Dependencies** - No extra dependencies
|
|
17
|
+
- 🔄 **Dual Module Support** - Works with both ESM and CommonJS
|
|
18
|
+
- 🎭 **Flexible Mocking** - Support for partial matching, strict matching, and custom handlers
|
|
19
|
+
- 🧩 **Chainable API** - Fluent interface for configuring multiple mock behaviors
|
|
20
|
+
- 🔍 **Custom Matchers** - Vitest matchers for asserting AWS SDK command calls
|
|
21
|
+
|
|
22
|
+
## 📦 Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
bun add -D aws-sdk-vitest-mock
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or with other package managers:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install --save-dev aws-sdk-vitest-mock
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
yarn add -D aws-sdk-vitest-mock
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pnpm add -D aws-sdk-vitest-mock
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## 🚀 Quick Start
|
|
43
|
+
|
|
44
|
+
### Basic Usage
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { mockClient } from "aws-sdk-vitest-mock";
|
|
48
|
+
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
|
|
49
|
+
|
|
50
|
+
// Mock the S3 client
|
|
51
|
+
const s3Mock = mockClient(S3Client);
|
|
52
|
+
|
|
53
|
+
// Configure mock responses
|
|
54
|
+
s3Mock.on(GetObjectCommand).resolves({
|
|
55
|
+
Body: "mock data",
|
|
56
|
+
ContentType: "text/plain",
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Use in your tests
|
|
60
|
+
const client = new S3Client({});
|
|
61
|
+
const result = await client.send(
|
|
62
|
+
new GetObjectCommand({
|
|
63
|
+
Bucket: "my-bucket",
|
|
64
|
+
Key: "my-key",
|
|
65
|
+
}),
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
console.log(result.Body); // 'mock data'
|
|
69
|
+
|
|
70
|
+
// Clean up
|
|
71
|
+
s3Mock.restore();
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Request Matching
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
// Partial matching (default)
|
|
78
|
+
s3Mock.on(GetObjectCommand, { Bucket: "bucket1" }).resolves({ Body: "data1" });
|
|
79
|
+
|
|
80
|
+
s3Mock.on(GetObjectCommand, { Bucket: "bucket2" }).resolves({ Body: "data2" });
|
|
81
|
+
|
|
82
|
+
// Strict matching
|
|
83
|
+
s3Mock
|
|
84
|
+
.on(GetObjectCommand, { Bucket: "b", Key: "k" }, { strict: true })
|
|
85
|
+
.resolves({ Body: "exact match" });
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Sequential Responses
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
s3Mock
|
|
92
|
+
.on(GetObjectCommand)
|
|
93
|
+
.resolvesOnce({ Body: "first call" })
|
|
94
|
+
.resolvesOnce({ Body: "second call" })
|
|
95
|
+
.resolves({ Body: "subsequent calls" });
|
|
96
|
+
|
|
97
|
+
// First call returns 'first call'
|
|
98
|
+
// Second call returns 'second call'
|
|
99
|
+
// All other calls return 'subsequent calls'
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Error Handling
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
s3Mock.on(GetObjectCommand).rejects(new Error("Not found"));
|
|
106
|
+
|
|
107
|
+
// Or with rejectsOnce
|
|
108
|
+
s3Mock
|
|
109
|
+
.on(GetObjectCommand)
|
|
110
|
+
.rejectsOnce(new Error("Temporary failure"))
|
|
111
|
+
.resolves({ Body: "success" });
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Custom Handlers
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
s3Mock.on(GetObjectCommand).callsFake(async (input, getClient) => {
|
|
118
|
+
const client = getClient();
|
|
119
|
+
console.log("Bucket:", input.Bucket);
|
|
120
|
+
return { Body: `Dynamic response for ${input.Key}` };
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Mocking Existing Instances
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
const existingClient = new S3Client({ region: "us-east-1" });
|
|
128
|
+
const mock = mockClientInstance(existingClient);
|
|
129
|
+
|
|
130
|
+
mock.on(GetObjectCommand).resolves({ Body: "mocked" });
|
|
131
|
+
|
|
132
|
+
// The existing instance is now mocked
|
|
133
|
+
const result = await existingClient.send(
|
|
134
|
+
new GetObjectCommand({
|
|
135
|
+
Bucket: "b",
|
|
136
|
+
Key: "k",
|
|
137
|
+
}),
|
|
138
|
+
);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## 🧪 Custom Matchers
|
|
142
|
+
|
|
143
|
+
Import the custom matchers in your test setup:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
// vitest.setup.ts
|
|
147
|
+
import "aws-sdk-vitest-mock/vitest-setup";
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Then use them in your tests:
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
import { expect, test } from "vitest";
|
|
154
|
+
import { mockClient } from "aws-sdk-vitest-mock";
|
|
155
|
+
import { DynamoDBClient, GetItemCommand } from "@aws-sdk/client-dynamodb";
|
|
156
|
+
|
|
157
|
+
test("should call DynamoDB", async () => {
|
|
158
|
+
const ddbMock = mockClient(DynamoDBClient);
|
|
159
|
+
ddbMock.on(GetItemCommand).resolves({ Item: { id: { S: "123" } } });
|
|
160
|
+
|
|
161
|
+
const client = new DynamoDBClient({});
|
|
162
|
+
await client.send(
|
|
163
|
+
new GetItemCommand({
|
|
164
|
+
TableName: "users",
|
|
165
|
+
Key: { id: { S: "123" } },
|
|
166
|
+
}),
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
// Assert the command was called
|
|
170
|
+
expect(ddbMock).toHaveReceivedCommand(GetItemCommand);
|
|
171
|
+
|
|
172
|
+
// Assert it was called a specific number of times
|
|
173
|
+
expect(ddbMock).toHaveReceivedCommandTimes(GetItemCommand, 1);
|
|
174
|
+
|
|
175
|
+
// Assert it was called with specific input
|
|
176
|
+
expect(ddbMock).toHaveReceivedCommandWith(GetItemCommand, {
|
|
177
|
+
TableName: "users",
|
|
178
|
+
Key: { id: { S: "123" } },
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Assert the nth call had specific input
|
|
182
|
+
expect(ddbMock).toHaveReceivedNthCommandWith(1, GetItemCommand, {
|
|
183
|
+
TableName: "users",
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## 📚 API Reference
|
|
189
|
+
|
|
190
|
+
### `mockClient<TClient>(ClientConstructor)`
|
|
191
|
+
|
|
192
|
+
Creates a mock for an AWS SDK client constructor.
|
|
193
|
+
|
|
194
|
+
**Returns:** `AwsClientStub<TClient>`
|
|
195
|
+
|
|
196
|
+
### `mockClientInstance<TClient>(clientInstance)`
|
|
197
|
+
|
|
198
|
+
Mocks an existing AWS SDK client instance.
|
|
199
|
+
|
|
200
|
+
**Returns:** `AwsClientStub<TClient>`
|
|
201
|
+
|
|
202
|
+
### `AwsClientStub` Methods
|
|
203
|
+
|
|
204
|
+
- `on(Command, matcher?, options?)` - Configure mock for a command
|
|
205
|
+
- `reset()` - Clear all mocks and call history
|
|
206
|
+
- `restore()` - Restore original client behavior
|
|
207
|
+
- `calls()` - Get call history
|
|
208
|
+
|
|
209
|
+
### `AwsCommandStub` Methods (Chainable)
|
|
210
|
+
|
|
211
|
+
- `resolves(output)` - Return successful response
|
|
212
|
+
- `resolvesOnce(output)` - Return successful response once
|
|
213
|
+
- `rejects(error)` - Return error
|
|
214
|
+
- `rejectsOnce(error)` - Return error once
|
|
215
|
+
- `callsFake(handler)` - Custom response handler
|
|
216
|
+
- `callsFakeOnce(handler)` - Custom response handler (once)
|
|
217
|
+
|
|
218
|
+
## 🤝 Contributing
|
|
219
|
+
|
|
220
|
+
We welcome contributions! 🎉 Please read our [Contributing Guidelines](./CONTRIBUTING.md) for details on:
|
|
221
|
+
|
|
222
|
+
- 🐛 Reporting bugs
|
|
223
|
+
- 💡 Suggesting features
|
|
224
|
+
- 🔧 Development setup
|
|
225
|
+
- ✅ Code standards
|
|
226
|
+
- 📝 Commit guidelines
|
|
227
|
+
- 🚀 Pull request process
|
|
228
|
+
|
|
229
|
+
### Quick Start for Contributors
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
# Fork and clone the repo
|
|
233
|
+
git clone https://github.com/YOUR-USERNAME/aws-sdk-vitest-mock.git
|
|
234
|
+
cd aws-sdk-vitest-mock
|
|
235
|
+
|
|
236
|
+
# Install dependencies
|
|
237
|
+
bun install
|
|
238
|
+
|
|
239
|
+
# Run tests
|
|
240
|
+
bun nx test
|
|
241
|
+
|
|
242
|
+
# Run linting
|
|
243
|
+
bun nx lint
|
|
244
|
+
|
|
245
|
+
# Build the library
|
|
246
|
+
bun nx build
|
|
247
|
+
|
|
248
|
+
# Make your changes and submit a PR!
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for the complete guide.
|
|
252
|
+
|
|
253
|
+
## 📝 License
|
|
254
|
+
|
|
255
|
+
MIT
|
|
256
|
+
|
|
257
|
+
## 🔗 Links
|
|
258
|
+
|
|
259
|
+
- [GitHub Repository](https://github.com/sudokar/aws-sdk-vitest-mock)
|
|
260
|
+
- [Issue Tracker](https://github.com/sudokar/aws-sdk-vitest-mock/issues)
|
|
261
|
+
- [Changelog](https://github.com/sudokar/aws-sdk-vitest-mock/releases)
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
**Made with ❤️ by [sudokar](https://github.com/sudokar)**
|
package/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("vitest"),h=require("./matchers-D1XEjFLq.cjs");function m(r,t){return Object.keys(t).every(c=>{const o=t[c],n=r[c];return o&&typeof o=="object"&&!Array.isArray(o)?typeof n!="object"||n===null?!1:m(n,o):n===o})}function y(r,t){if(r===t)return!0;if(typeof r!="object"||r===null||typeof t!="object"||t===null)return r===t;const c=Object.keys(r),o=Object.keys(t);return c.length!==o.length?!1:o.every(n=>{if(!Object.prototype.hasOwnProperty.call(r,n))return!1;const s=r[n],e=t[n];return typeof s=="object"&&s!==null&&typeof e=="object"&&e!==null?y(s,e):s===e})}function k(r){return async function(t){const c=this,o=()=>c,n=r.map.get(t.constructor);if(n){const s=n.findIndex(e=>e.strict?e.matcher&&y(t.input,e.matcher):!e.matcher||m(t.input,e.matcher));if(s!==-1){const e=n[s];return e.once&&n.splice(s,1),e.handler(t.input,o)}}throw new Error(`No mock configured for command: ${t.constructor.name}`)}}function b(r,t,c,o={}){const n=(e,a)=>{const p={matcher:c,handler:e,once:a,strict:!!o.strict},i=r.map.get(t)??[];if(a){const l=i.findIndex(u=>!u.once);l===-1?i.push(p):i.splice(l,0,p),r.map.set(t,i)}else{const l=i.filter(u=>u.once||JSON.stringify(u.matcher)!==JSON.stringify(c));l.push(p),r.map.set(t,l)}},s={resolves(e){return n(()=>Promise.resolve(e),!1),s},rejects(e){return n(()=>{const a=typeof e=="string"?new Error(e):e;return Promise.reject(a)},!1),s},callsFake(e){return n(e,!1),s},resolvesOnce(e){return n(()=>Promise.resolve(e),!0),s},rejectsOnce(e){return n(()=>{const a=typeof e=="string"?new Error(e):e;return Promise.reject(a)},!0),s},callsFakeOnce(e){return n(e,!0),s}};return s}const j=r=>{const t={map:new WeakMap},c=("prototype"in r,r.prototype),o=f.vi.spyOn(c,"send").mockImplementation(k(t));return{client:void 0,on:(s,e,a)=>b(t,s,e,a),reset:()=>{o.mockClear(),t.map=new WeakMap},restore:()=>{o.mockRestore(),t.map=new WeakMap},calls:()=>o.mock.calls}},d=r=>{const t={map:new WeakMap},c=f.vi.spyOn(r,"send").mockImplementation(k(t));return{client:r,on:(n,s,e)=>b(t,n,s,e),reset:()=>{c.mockClear(),t.map=new WeakMap},restore:()=>{c.mockRestore(),t.map=new WeakMap},calls:()=>c.mock.calls}};exports.matchers=h.matchers;exports.mockClient=j;exports.mockClientInstance=d;
|
package/index.d.ts
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { vi as f } from "vitest";
|
|
2
|
+
import { m as w } from "./matchers-DclpcT4f.js";
|
|
3
|
+
function m(r, t) {
|
|
4
|
+
return Object.keys(t).every((c) => {
|
|
5
|
+
const o = t[c], n = r[c];
|
|
6
|
+
return o && typeof o == "object" && !Array.isArray(o) ? typeof n != "object" || n === null ? !1 : m(n, o) : n === o;
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
function y(r, t) {
|
|
10
|
+
if (r === t) return !0;
|
|
11
|
+
if (typeof r != "object" || r === null || typeof t != "object" || t === null)
|
|
12
|
+
return r === t;
|
|
13
|
+
const c = Object.keys(r), o = Object.keys(t);
|
|
14
|
+
return c.length !== o.length ? !1 : o.every((n) => {
|
|
15
|
+
if (!Object.prototype.hasOwnProperty.call(r, n)) return !1;
|
|
16
|
+
const s = r[n], e = t[n];
|
|
17
|
+
return typeof s == "object" && s !== null && typeof e == "object" && e !== null ? y(s, e) : s === e;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
function k(r) {
|
|
21
|
+
return async function(t) {
|
|
22
|
+
const c = this, o = () => c, n = r.map.get(t.constructor);
|
|
23
|
+
if (n) {
|
|
24
|
+
const s = n.findIndex((e) => e.strict ? e.matcher && y(t.input, e.matcher) : !e.matcher || m(t.input, e.matcher));
|
|
25
|
+
if (s !== -1) {
|
|
26
|
+
const e = n[s];
|
|
27
|
+
return e.once && n.splice(s, 1), e.handler(t.input, o);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
throw new Error(`No mock configured for command: ${t.constructor.name}`);
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function b(r, t, c, o = {}) {
|
|
34
|
+
const n = (e, a) => {
|
|
35
|
+
const p = {
|
|
36
|
+
matcher: c,
|
|
37
|
+
handler: e,
|
|
38
|
+
once: a,
|
|
39
|
+
strict: !!o.strict
|
|
40
|
+
}, i = r.map.get(t) ?? [];
|
|
41
|
+
if (a) {
|
|
42
|
+
const l = i.findIndex((u) => !u.once);
|
|
43
|
+
l === -1 ? i.push(p) : i.splice(l, 0, p), r.map.set(t, i);
|
|
44
|
+
} else {
|
|
45
|
+
const l = i.filter(
|
|
46
|
+
(u) => u.once || JSON.stringify(u.matcher) !== JSON.stringify(c)
|
|
47
|
+
);
|
|
48
|
+
l.push(p), r.map.set(t, l);
|
|
49
|
+
}
|
|
50
|
+
}, s = {
|
|
51
|
+
resolves(e) {
|
|
52
|
+
return n(() => Promise.resolve(e), !1), s;
|
|
53
|
+
},
|
|
54
|
+
rejects(e) {
|
|
55
|
+
return n(() => {
|
|
56
|
+
const a = typeof e == "string" ? new Error(e) : e;
|
|
57
|
+
return Promise.reject(a);
|
|
58
|
+
}, !1), s;
|
|
59
|
+
},
|
|
60
|
+
callsFake(e) {
|
|
61
|
+
return n(e, !1), s;
|
|
62
|
+
},
|
|
63
|
+
resolvesOnce(e) {
|
|
64
|
+
return n(() => Promise.resolve(e), !0), s;
|
|
65
|
+
},
|
|
66
|
+
rejectsOnce(e) {
|
|
67
|
+
return n(() => {
|
|
68
|
+
const a = typeof e == "string" ? new Error(e) : e;
|
|
69
|
+
return Promise.reject(a);
|
|
70
|
+
}, !0), s;
|
|
71
|
+
},
|
|
72
|
+
callsFakeOnce(e) {
|
|
73
|
+
return n(e, !0), s;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
return s;
|
|
77
|
+
}
|
|
78
|
+
const j = (r) => {
|
|
79
|
+
const t = {
|
|
80
|
+
map: /* @__PURE__ */ new WeakMap()
|
|
81
|
+
}, c = ("prototype" in r, r.prototype), o = f.spyOn(c, "send").mockImplementation(k(t));
|
|
82
|
+
return {
|
|
83
|
+
client: void 0,
|
|
84
|
+
on: (s, e, a) => b(t, s, e, a),
|
|
85
|
+
reset: () => {
|
|
86
|
+
o.mockClear(), t.map = /* @__PURE__ */ new WeakMap();
|
|
87
|
+
},
|
|
88
|
+
restore: () => {
|
|
89
|
+
o.mockRestore(), t.map = /* @__PURE__ */ new WeakMap();
|
|
90
|
+
},
|
|
91
|
+
calls: () => o.mock.calls
|
|
92
|
+
};
|
|
93
|
+
}, d = (r) => {
|
|
94
|
+
const t = {
|
|
95
|
+
map: /* @__PURE__ */ new WeakMap()
|
|
96
|
+
}, c = f.spyOn(r, "send").mockImplementation(k(t));
|
|
97
|
+
return {
|
|
98
|
+
client: r,
|
|
99
|
+
on: (n, s, e) => b(t, n, s, e),
|
|
100
|
+
reset: () => {
|
|
101
|
+
c.mockClear(), t.map = /* @__PURE__ */ new WeakMap();
|
|
102
|
+
},
|
|
103
|
+
restore: () => {
|
|
104
|
+
c.mockRestore(), t.map = /* @__PURE__ */ new WeakMap();
|
|
105
|
+
},
|
|
106
|
+
calls: () => c.mock.calls
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
export {
|
|
110
|
+
w as matchers,
|
|
111
|
+
j as mockClient,
|
|
112
|
+
d as mockClientInstance
|
|
113
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AwsClientStub } from './mock-client.js';
|
|
2
|
+
type CommandConstructor = new (...args: unknown[]) => unknown;
|
|
3
|
+
interface MatcherResult {
|
|
4
|
+
pass: boolean;
|
|
5
|
+
message: () => string;
|
|
6
|
+
}
|
|
7
|
+
export declare const matchers: {
|
|
8
|
+
toHaveReceivedCommand(received: AwsClientStub, command: CommandConstructor): MatcherResult;
|
|
9
|
+
toHaveReceivedCommandTimes(received: AwsClientStub, command: CommandConstructor, times: number): MatcherResult;
|
|
10
|
+
toHaveReceivedCommandWith(this: {
|
|
11
|
+
equals: (a: unknown, b: unknown) => boolean;
|
|
12
|
+
}, received: AwsClientStub, command: CommandConstructor, input: Record<string, unknown>): MatcherResult;
|
|
13
|
+
toHaveReceivedNthCommandWith(this: {
|
|
14
|
+
equals: (a: unknown, b: unknown) => boolean;
|
|
15
|
+
}, received: AwsClientStub, n: number, command: CommandConstructor, input: Record<string, unknown>): MatcherResult;
|
|
16
|
+
};
|
|
17
|
+
export interface AwsSdkMatchers<R = unknown> {
|
|
18
|
+
toHaveReceivedCommand(command: CommandConstructor): R;
|
|
19
|
+
toHaveReceivedCommandTimes(command: CommandConstructor, times: number): R;
|
|
20
|
+
toHaveReceivedCommandWith(command: CommandConstructor, input: Record<string, unknown>): R;
|
|
21
|
+
toHaveReceivedNthCommandWith(n: number, command: CommandConstructor, input: Record<string, unknown>): R;
|
|
22
|
+
}
|
|
23
|
+
declare module 'vitest' {
|
|
24
|
+
interface Assertion extends AwsSdkMatchers {
|
|
25
|
+
}
|
|
26
|
+
interface AsymmetricMatchersContaining extends AwsSdkMatchers {
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { SmithyResolvedConfiguration } from '@smithy/smithy-client';
|
|
2
|
+
import { MiddlewareStack, Handler } from '@smithy/types';
|
|
3
|
+
import { Mock } from 'vitest';
|
|
4
|
+
export interface MetadataBearer {
|
|
5
|
+
$metadata?: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface StructuralCommand<TInput extends object, TOutput extends MetadataBearer> {
|
|
8
|
+
readonly input: TInput;
|
|
9
|
+
middlewareStack: MiddlewareStack<TInput, TOutput>;
|
|
10
|
+
resolveMiddleware(stack: MiddlewareStack<TInput, TOutput>, configuration: unknown, options: unknown): Handler<TInput, TOutput>;
|
|
11
|
+
}
|
|
12
|
+
export type CommandConstructor<TInput extends object, TOutput extends MetadataBearer> = new (input: TInput) => StructuralCommand<TInput, TOutput>;
|
|
13
|
+
export type AnyClient = {
|
|
14
|
+
send(command: AnyCommand): Promise<MetadataBearer>;
|
|
15
|
+
config: SmithyResolvedConfiguration<unknown>;
|
|
16
|
+
};
|
|
17
|
+
export type AnyCommand = StructuralCommand<object, MetadataBearer>;
|
|
18
|
+
export type ClientConstructor<TClient extends AnyClient> = (abstract new (config: unknown) => TClient) | {
|
|
19
|
+
prototype: TClient;
|
|
20
|
+
};
|
|
21
|
+
type CommandHandler<TInput extends object = object, TOutput extends MetadataBearer = MetadataBearer, TClient extends AnyClient = AnyClient> = (input: TInput, getClient: () => TClient | undefined) => Promise<TOutput>;
|
|
22
|
+
interface MockOptions {
|
|
23
|
+
strict?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface AwsClientStub<TClient extends AnyClient = AnyClient> {
|
|
26
|
+
readonly client: TClient | undefined;
|
|
27
|
+
on: <TInput extends object, TOutput extends MetadataBearer>(command: CommandConstructor<TInput, TOutput>, request?: Partial<TInput>, options?: MockOptions) => AwsCommandStub<TInput, TOutput, TClient>;
|
|
28
|
+
reset: () => void;
|
|
29
|
+
restore: () => void;
|
|
30
|
+
calls: () => ReturnType<Mock['mock']['calls']['slice']>;
|
|
31
|
+
}
|
|
32
|
+
export interface AwsCommandStub<TInput extends object, TOutput extends MetadataBearer, TClient extends AnyClient = AnyClient> {
|
|
33
|
+
/** Set a permanent mock response (used after all once handlers are consumed) */
|
|
34
|
+
resolves: (output: Partial<TOutput>) => AwsCommandStub<TInput, TOutput, TClient>;
|
|
35
|
+
/** Set a permanent mock rejection (used after all once handlers are consumed) */
|
|
36
|
+
rejects: (error: Error | string) => AwsCommandStub<TInput, TOutput, TClient>;
|
|
37
|
+
/** Set a permanent custom handler (used after all once handlers are consumed) */
|
|
38
|
+
callsFake: (fn: CommandHandler<TInput, TOutput, TClient>) => AwsCommandStub<TInput, TOutput, TClient>;
|
|
39
|
+
/** Add a one-time mock response (consumed in order) */
|
|
40
|
+
resolvesOnce: (output: Partial<TOutput>) => AwsCommandStub<TInput, TOutput, TClient>;
|
|
41
|
+
/** Add a one-time mock rejection (consumed in order) */
|
|
42
|
+
rejectsOnce: (error: Error | string) => AwsCommandStub<TInput, TOutput, TClient>;
|
|
43
|
+
/** Add a one-time custom handler (consumed in order) */
|
|
44
|
+
callsFakeOnce: (fn: CommandHandler<TInput, TOutput, TClient>) => AwsCommandStub<TInput, TOutput, TClient>;
|
|
45
|
+
}
|
|
46
|
+
export declare const mockClient: <TClient extends AnyClient>(clientConstructor: ClientConstructor<TClient>) => AwsClientStub<TClient>;
|
|
47
|
+
export declare const mockClientInstance: <TClient extends AnyClient>(clientInstance: TClient) => AwsClientStub<AnyClient>;
|
|
48
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const d={toHaveReceivedCommand(n,e){const t=n.calls().some(a=>a[0]instanceof e),s=e.name;return{pass:t,message:()=>t?`Expected AWS SDK mock not to have received command ${s}`:`Expected AWS SDK mock to have received command ${s}`}},toHaveReceivedCommandTimes(n,e,c){const t=n.calls().filter(o=>o[0]instanceof e),s=t.length===c,a=e.name;return{pass:s,message:()=>s?`Expected AWS SDK mock not to have received command ${a} ${c} times`:`Expected AWS SDK mock to have received command ${a} ${c} times, but received ${t.length} times`}},toHaveReceivedCommandWith(n,e,c){const s=n.calls().filter(o=>o[0]instanceof e).some(o=>this.equals(o[0].input,c)),a=e.name;return{pass:s,message:()=>s?`Expected AWS SDK mock not to have received command ${a} with ${JSON.stringify(c)}`:`Expected AWS SDK mock to have received command ${a} with ${JSON.stringify(c)}`}},toHaveReceivedNthCommandWith(n,e,c,t){const a=n.calls().filter(i=>i[0]instanceof c)[e-1],o=!!(a&&this.equals(a[0].input,t)),m=c.name;return{pass:o,message:()=>o?`Expected AWS SDK mock not to have received nth (${e}) command ${m} with ${JSON.stringify(t)}`:`Expected AWS SDK mock to have received nth (${e}) command ${m} with ${JSON.stringify(t)}`}}};exports.matchers=d;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const d = {
|
|
2
|
+
toHaveReceivedCommand(n, e) {
|
|
3
|
+
const t = n.calls().some((a) => a[0] instanceof e), o = e.name;
|
|
4
|
+
return {
|
|
5
|
+
pass: t,
|
|
6
|
+
message: () => t ? `Expected AWS SDK mock not to have received command ${o}` : `Expected AWS SDK mock to have received command ${o}`
|
|
7
|
+
};
|
|
8
|
+
},
|
|
9
|
+
toHaveReceivedCommandTimes(n, e, c) {
|
|
10
|
+
const t = n.calls().filter((s) => s[0] instanceof e), o = t.length === c, a = e.name;
|
|
11
|
+
return {
|
|
12
|
+
pass: o,
|
|
13
|
+
message: () => o ? `Expected AWS SDK mock not to have received command ${a} ${c} times` : `Expected AWS SDK mock to have received command ${a} ${c} times, but received ${t.length} times`
|
|
14
|
+
};
|
|
15
|
+
},
|
|
16
|
+
toHaveReceivedCommandWith(n, e, c) {
|
|
17
|
+
const o = n.calls().filter((s) => s[0] instanceof e).some((s) => this.equals(s[0].input, c)), a = e.name;
|
|
18
|
+
return {
|
|
19
|
+
pass: o,
|
|
20
|
+
message: () => o ? `Expected AWS SDK mock not to have received command ${a} with ${JSON.stringify(c)}` : `Expected AWS SDK mock to have received command ${a} with ${JSON.stringify(c)}`
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
toHaveReceivedNthCommandWith(n, e, c, t) {
|
|
24
|
+
const a = n.calls().filter((i) => i[0] instanceof c)[e - 1], s = !!(a && this.equals(a[0].input, t)), m = c.name;
|
|
25
|
+
return {
|
|
26
|
+
pass: s,
|
|
27
|
+
message: () => s ? `Expected AWS SDK mock not to have received nth (${e}) command ${m} with ${JSON.stringify(t)}` : `Expected AWS SDK mock to have received nth (${e}) command ${m} with ${JSON.stringify(t)}`
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
export {
|
|
32
|
+
d as m
|
|
33
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aws-sdk-vitest-mock",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./index.cjs",
|
|
6
|
+
"module": "./index.js",
|
|
7
|
+
"types": "./index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./index.d.ts",
|
|
12
|
+
"default": "./index.js"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./index.d.ts",
|
|
16
|
+
"default": "./index.cjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"./vitest-setup": {
|
|
20
|
+
"import": "./vitest-setup.js",
|
|
21
|
+
"require": "./vitest-setup.cjs"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"author": "sudokar",
|
|
26
|
+
"scripts": {
|
|
27
|
+
"prepare": "husky",
|
|
28
|
+
"prepublishOnly": "HUSKY=0"
|
|
29
|
+
},
|
|
30
|
+
"nx": {
|
|
31
|
+
"includedScripts": []
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@aws-sdk/client-dynamodb": "3.948.0",
|
|
36
|
+
"@aws-sdk/client-s3": "3.948.0",
|
|
37
|
+
"@aws-sdk/lib-dynamodb": "3.948.0",
|
|
38
|
+
"@aws-sdk/types": "3.936.0",
|
|
39
|
+
"@eslint/js": "9.39.1",
|
|
40
|
+
"@nx/eslint": "22.2.2",
|
|
41
|
+
"@nx/eslint-plugin": "22.2.2",
|
|
42
|
+
"@nx/js": "22.2.2",
|
|
43
|
+
"@nx/vite": "22.2.2",
|
|
44
|
+
"@nx/vitest": "22.2.2",
|
|
45
|
+
"@nx/workspace": "22.2.2",
|
|
46
|
+
"@smithy/types": "4.9.0",
|
|
47
|
+
"@swc-node/register": "1.11.1",
|
|
48
|
+
"@swc/core": "1.15.3",
|
|
49
|
+
"@swc/helpers": "0.5.17",
|
|
50
|
+
"@types/node": "24.10.3",
|
|
51
|
+
"@typescript-eslint/parser": "8.49.0",
|
|
52
|
+
"@vitest/coverage-v8": "4.0.15",
|
|
53
|
+
"@vitest/eslint-plugin": "1.5.2",
|
|
54
|
+
"eslint": "9.39.1",
|
|
55
|
+
"eslint-config-prettier": "10.1.8",
|
|
56
|
+
"eslint-plugin-import": "2.32.0",
|
|
57
|
+
"eslint-plugin-security": "3.0.1",
|
|
58
|
+
"eslint-plugin-sonarjs": "3.0.5",
|
|
59
|
+
"eslint-plugin-unicorn": "62.0.0",
|
|
60
|
+
"husky": "9.1.7",
|
|
61
|
+
"lint-staged": "16.2.7",
|
|
62
|
+
"nx": "22.2.2",
|
|
63
|
+
"prettier": "3.7.4",
|
|
64
|
+
"typescript": "5.9.3",
|
|
65
|
+
"typescript-eslint": "8.49.0",
|
|
66
|
+
"vite": "7.2.7",
|
|
67
|
+
"vite-plugin-dts": "4.5.4",
|
|
68
|
+
"vitest": "4.0.15"
|
|
69
|
+
},
|
|
70
|
+
"peerDependencies": {
|
|
71
|
+
"@aws-sdk/core": "^3",
|
|
72
|
+
"vitest": "^4"
|
|
73
|
+
}
|
|
74
|
+
}
|
package/vitest-setup.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const e=require("vitest"),t=require("./matchers-D1XEjFLq.cjs");e.expect.extend(t.matchers);
|
package/vitest-setup.js
ADDED