Skip to content

Commit d56a451

Browse files
committed
test(lib-storage): example of how to mock requests for MPU
1 parent da2bf71 commit d56a451

File tree

3 files changed

+291
-1
lines changed

3 files changed

+291
-1
lines changed

lib/lib-storage/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
"clean": "rimraf ./dist-* && rimraf *.tsbuildinfo",
1616
"extract:docs": "api-extractor run --local",
1717
"test": "yarn g:vitest run",
18-
"test:e2e": "yarn g:vitest run -c vitest.config.e2e.mts --mode development",
1918
"test:watch": "yarn g:vitest watch",
19+
"test:integration": "yarn g:vitest run -c vitest.config.integ.mts",
20+
"test:integration:watch": "yarn g:vitest watch -c vitest.config.integ.mts",
21+
"test:e2e": "yarn g:vitest run -c vitest.config.e2e.mts --mode development",
2022
"test:e2e:watch": "yarn g:vitest watch -c vitest.config.e2e.mts"
2123
},
2224
"engines": {
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
import { getE2eTestResources, requireRequestsFrom } from "@aws-sdk/aws-util-test/src";
2+
import { S3 } from "@aws-sdk/client-s3";
3+
import { Upload } from "@aws-sdk/lib-storage";
4+
import { getHttpDebugLogPlugin } from "@aws-sdk/middleware-http-debug-log/src";
5+
import { HttpResponse } from "@smithy/protocol-http";
6+
import { randomBytes } from "crypto";
7+
import { Readable } from "node:stream";
8+
import { describe, expect, test as it } from "vitest";
9+
10+
describe("lib storage integration test", () => {
11+
const example = async () => {
12+
const e2eTestResourcesEnv = await getE2eTestResources();
13+
Object.assign(process.env, e2eTestResourcesEnv);
14+
15+
const region = process?.env?.AWS_SMOKE_TEST_REGION as string;
16+
const Bucket = process?.env?.AWS_SMOKE_TEST_BUCKET as string;
17+
const data = randomBytes(6 * 1024 * 1024);
18+
19+
const s3 = new S3({
20+
region,
21+
});
22+
const Key = `MPU-${Date.now()}`;
23+
24+
const client = new S3({
25+
region,
26+
});
27+
// This will print out all requests and responses so you can use
28+
// them in an integration mock later.
29+
client.middlewareStack.use(
30+
getHttpDebugLogPlugin({
31+
request: {
32+
url: true,
33+
command: true,
34+
method: true,
35+
},
36+
response: {
37+
statusCode: true,
38+
headers: true,
39+
body: true,
40+
formatBody: true,
41+
},
42+
})
43+
);
44+
45+
await new Upload({
46+
client,
47+
params: {
48+
Bucket,
49+
Key,
50+
Body: data,
51+
},
52+
}).done();
53+
54+
await s3.deleteObject({
55+
Bucket,
56+
Key,
57+
});
58+
};
59+
60+
it("example of how to write an integration test that includes responses", async () => {
61+
const client = new S3({
62+
region: "us-west-2",
63+
credentials: {
64+
accessKeyId: "INTEG",
65+
secretAccessKey: "INTEG",
66+
},
67+
});
68+
69+
const commandOutputs: Record<string, any[]> = {};
70+
71+
client.middlewareStack.add((next, context) => async (args) => {
72+
const r = await next(args);
73+
commandOutputs[context.commandName!] = commandOutputs[context.commandName!] ?? [];
74+
commandOutputs[context.commandName!].push(r.output);
75+
return r;
76+
});
77+
78+
requireRequestsFrom(client)
79+
.toMatch({
80+
hostname: /amazon/,
81+
})
82+
.respondWith(
83+
new HttpResponse({
84+
statusCode: 200,
85+
headers: {
86+
"x-amz-id-2":
87+
"bKbXrk1IXbqfupvsn8gGtkGi33Nszcq9iwiah4xGeydCedkuKWeht6xnBkn0sCBhVDyDs0Xa4ecdbnxtyzMGqc17Cv6Se7P8",
88+
"x-amz-request-id": "MC075MYM6KAT5AQE",
89+
date: "Fri, 26 Sep 2025 17:27:23 GMT",
90+
"x-amz-server-side-encryption": "AES256",
91+
"x-amz-checksum-algorithm": "CRC32",
92+
"x-amz-checksum-type": "COMPOSITE",
93+
"transfer-encoding": "chunked",
94+
server: "AmazonS3",
95+
},
96+
body: Readable.from(
97+
Buffer.from(
98+
`<?xml version="1.0" encoding="UTF-8"?>
99+
<InitiateMultipartUploadResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
100+
<Bucket>
101+
sdkreleasev3integtestreso-integtestbucketa93771ae-zh5lrv1xnwjx
102+
</Bucket>
103+
<Key>
104+
MPU-1758907641953
105+
</Key>
106+
<UploadId>
107+
FPyQ2V.AnlVZcN3GUqieu5Ael9CllYWycVH0slQyiS9wYjDeUKS0okoMm.jbbbmMbNln.K8HtPzbwjCChgCUH9B94b6MyrD72_auD23tEpXhmel40UtdL.7w_RiNAc1xkyr8ooIKRsGyDE9J.WIH0Q--
108+
</UploadId>
109+
</InitiateMultipartUploadResult>`
110+
)
111+
),
112+
}),
113+
new HttpResponse({
114+
statusCode: 200,
115+
headers: {
116+
"x-amz-id-2":
117+
"1ezf9iJF3YvPWo3SF2UCrgFGBXltd25g9bHUA9W4k58PJ/W03OZ13nIOEmGE+NCRCbmuERJ4lvML5zGQU0JCw44sOelC9sRb",
118+
"x-amz-request-id": "MC01ZVGGQ8Z7950C",
119+
date: "Fri, 26 Sep 2025 17:27:23 GMT",
120+
etag: '"eb3760d36bc660b509833238c0799b58"',
121+
"x-amz-checksum-crc32": "Ikyd7A==",
122+
"x-amz-server-side-encryption": "AES256",
123+
"content-length": "0",
124+
server: "AmazonS3",
125+
},
126+
}),
127+
new HttpResponse({
128+
statusCode: 200,
129+
headers: {
130+
"x-amz-id-2": "JwgQ5LZ9Sx3fj6G46KOjfx7HI2XvK18Nx6iCOQPH+/UFjbFPju3hlZ7Gq8BIW6g2IiyI8cM3v1LPh+Me8KmCZQ==",
131+
"x-amz-request-id": "MC0DH0R666ESRWE3",
132+
date: "Fri, 26 Sep 2025 17:27:23 GMT",
133+
etag: '"76d0701ab8175448d01476321416bf01"',
134+
"x-amz-checksum-crc32": "JTOG+w==",
135+
"x-amz-server-side-encryption": "AES256",
136+
"content-length": "0",
137+
server: "AmazonS3",
138+
},
139+
}),
140+
new HttpResponse({
141+
statusCode: 200,
142+
headers: {
143+
"x-amz-id-2": "0G3bPmuvsW9FMp4OCpbRUxtldh81E3PxbvhUuXsCtasMMpYVfKlxvYkWD9wekOxD/C0xay5ttt/d7MXxz79JBw==",
144+
"x-amz-request-id": "HY3KQKTR6ZKMGZ6E",
145+
date: "Fri, 26 Sep 2025 17:27:24 GMT",
146+
"x-amz-version-id": "PVmXZ_B1Qs3bTot7SY6w_.aiH3TpVbQ6",
147+
"x-amz-server-side-encryption": "AES256",
148+
"content-type": "application/xml",
149+
"transfer-encoding": "chunked",
150+
server: "AmazonS3",
151+
},
152+
body: Readable.from(
153+
Buffer.from(
154+
`<?xml version="1.0" encoding="UTF-8"?>
155+
<CompleteMultipartUploadResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
156+
<Location>
157+
https://bucket.s3.us-west-2.amazonaws.com/MPU-1758907641953
158+
</Location>
159+
<Bucket>
160+
bucket
161+
</Bucket>
162+
<Key>
163+
MPU-1758907641953
164+
</Key>
165+
<ETag>
166+
"e4cd0558ba33b2b33aa64f158deae527-2"
167+
</ETag>
168+
<ChecksumCRC32>
169+
53GakA==-2
170+
</ChecksumCRC32>
171+
<ChecksumType>
172+
COMPOSITE
173+
</ChecksumType>
174+
</CompleteMultipartUploadResult>`
175+
)
176+
),
177+
})
178+
);
179+
180+
const uploadOutput = await new Upload({
181+
client,
182+
params: {
183+
Bucket: "bucket",
184+
Key: "key",
185+
Body: randomBytes(6 * 1024 * 1024),
186+
},
187+
}).done();
188+
189+
/**
190+
* (Because the XML was given without trimming).
191+
*/
192+
function trimObject(item: any): any {
193+
if (typeof item === "string") {
194+
return item.trim();
195+
}
196+
if (Array.isArray(item)) {
197+
return item.map(trimObject);
198+
}
199+
if (item && typeof item === "object") {
200+
for (const key in item) {
201+
item[key] = trimObject(item[key]);
202+
}
203+
}
204+
return item;
205+
}
206+
trimObject(commandOutputs);
207+
208+
expect(commandOutputs).toEqual({
209+
CreateMultipartUploadCommand: [
210+
{
211+
$metadata: {
212+
httpStatusCode: 200,
213+
requestId: "MC075MYM6KAT5AQE",
214+
extendedRequestId:
215+
"bKbXrk1IXbqfupvsn8gGtkGi33Nszcq9iwiah4xGeydCedkuKWeht6xnBkn0sCBhVDyDs0Xa4ecdbnxtyzMGqc17Cv6Se7P8",
216+
attempts: 1,
217+
totalRetryDelay: 0,
218+
},
219+
ServerSideEncryption: "AES256",
220+
ChecksumAlgorithm: "CRC32",
221+
ChecksumType: "COMPOSITE",
222+
Bucket: "sdkreleasev3integtestreso-integtestbucketa93771ae-zh5lrv1xnwjx",
223+
Key: "MPU-1758907641953",
224+
UploadId:
225+
"FPyQ2V.AnlVZcN3GUqieu5Ael9CllYWycVH0slQyiS9wYjDeUKS0okoMm.jbbbmMbNln.K8HtPzbwjCChgCUH9B94b6MyrD72_auD23tEpXhmel40UtdL.7w_RiNAc1xkyr8ooIKRsGyDE9J.WIH0Q--",
226+
},
227+
],
228+
UploadPartCommand: [
229+
{
230+
$metadata: {
231+
httpStatusCode: 200,
232+
requestId: "MC01ZVGGQ8Z7950C",
233+
extendedRequestId:
234+
"1ezf9iJF3YvPWo3SF2UCrgFGBXltd25g9bHUA9W4k58PJ/W03OZ13nIOEmGE+NCRCbmuERJ4lvML5zGQU0JCw44sOelC9sRb",
235+
attempts: 1,
236+
totalRetryDelay: 0,
237+
},
238+
ServerSideEncryption: "AES256",
239+
ETag: '"eb3760d36bc660b509833238c0799b58"',
240+
ChecksumCRC32: "Ikyd7A==",
241+
},
242+
{
243+
$metadata: {
244+
httpStatusCode: 200,
245+
requestId: "MC0DH0R666ESRWE3",
246+
extendedRequestId:
247+
"JwgQ5LZ9Sx3fj6G46KOjfx7HI2XvK18Nx6iCOQPH+/UFjbFPju3hlZ7Gq8BIW6g2IiyI8cM3v1LPh+Me8KmCZQ==",
248+
attempts: 1,
249+
totalRetryDelay: 0,
250+
},
251+
ServerSideEncryption: "AES256",
252+
ETag: '"76d0701ab8175448d01476321416bf01"',
253+
ChecksumCRC32: "JTOG+w==",
254+
},
255+
],
256+
CompleteMultipartUploadCommand: [
257+
{
258+
$metadata: {
259+
httpStatusCode: 200,
260+
requestId: "HY3KQKTR6ZKMGZ6E",
261+
extendedRequestId:
262+
"0G3bPmuvsW9FMp4OCpbRUxtldh81E3PxbvhUuXsCtasMMpYVfKlxvYkWD9wekOxD/C0xay5ttt/d7MXxz79JBw==",
263+
attempts: 1,
264+
totalRetryDelay: 0,
265+
},
266+
ServerSideEncryption: "AES256",
267+
VersionId: "PVmXZ_B1Qs3bTot7SY6w_.aiH3TpVbQ6",
268+
Bucket: "bucket",
269+
ChecksumCRC32: "53GakA==-2",
270+
ChecksumType: "COMPOSITE",
271+
ETag: '"e4cd0558ba33b2b33aa64f158deae527-2"',
272+
Key: "MPU-1758907641953",
273+
Location: "https://bucket.s3.us-west-2.amazonaws.com/MPU-1758907641953",
274+
},
275+
],
276+
});
277+
278+
expect(uploadOutput).toMatchObject(commandOutputs.CompleteMultipartUploadCommand[0]);
279+
});
280+
}, 60_000);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { defineConfig } from "vitest/config";
2+
3+
export default defineConfig({
4+
test: {
5+
include: ["**/*.integ.spec.ts"],
6+
environment: "node",
7+
},
8+
});

0 commit comments

Comments
 (0)