Skip to content

Commit 14cd92e

Browse files
committed
test(client-s3): convert feature test to vitest
1 parent 24e5b28 commit 14cd92e

File tree

8 files changed

+647
-1053
lines changed

8 files changed

+647
-1053
lines changed
Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
import { S3, waitUntilBucketExists, waitUntilBucketNotExists } from "@aws-sdk/client-s3";
2+
import { type GetCallerIdentityCommandOutput, STS } from "@aws-sdk/client-sts";
3+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
4+
import { HttpRequest } from "@smithy/types";
5+
6+
describe("@aws-sdk/client-s3 - Working with Buckets", () => {
7+
const s3 = new S3({
8+
region: "us-west-2",
9+
});
10+
const s3East = new S3({
11+
region: "us-east-1",
12+
followRegionRedirects: true,
13+
});
14+
const s3PathStyle = new S3({
15+
region: "us-west-2",
16+
forcePathStyle: true,
17+
});
18+
const stsClient = new STS({
19+
region: "us-west-2",
20+
});
21+
22+
function getBucketName(id: string, region = "us-west-2") {
23+
const alphabet = "abcdefghijklmnopqrstuvwxyz";
24+
const randId = Array.from({ length: 6 }, () => alphabet[(Math.random() * alphabet.length) | 0]).join("");
25+
return `${callerID.Account}-${randId}-${id}-${region}-${(Date.now() / 1000) | 0}`;
26+
}
27+
28+
let Bucket: string;
29+
let callerID: GetCallerIdentityCommandOutput;
30+
31+
beforeAll(async () => {
32+
callerID = await stsClient.getCallerIdentity({});
33+
Bucket = getBucketName(`js-sdk-e2e`);
34+
});
35+
36+
describe("CRUD buckets using classic endpoint", () => {
37+
let bucketEast: string | undefined;
38+
39+
beforeAll(async () => {
40+
bucketEast = Bucket.replace("us-west-2", "us-east-1");
41+
});
42+
43+
afterAll(async () => {
44+
await s3East.deleteBucket({
45+
Bucket: bucketEast,
46+
});
47+
await waitUntilBucketNotExists(
48+
{
49+
client: s3East,
50+
maxWaitTime: 60,
51+
},
52+
{
53+
Bucket: bucketEast,
54+
}
55+
);
56+
});
57+
58+
it("should create and verify bucket in us-east-1", async () => {
59+
await s3East.createBucket({
60+
Bucket: bucketEast,
61+
});
62+
await waitUntilBucketExists(
63+
{
64+
client: s3East,
65+
maxWaitTime: 60,
66+
},
67+
{
68+
Bucket: bucketEast,
69+
}
70+
);
71+
await s3East.headBucket({ Bucket: bucketEast });
72+
});
73+
});
74+
75+
describe("CRUD buckets using regional endpoint", () => {
76+
afterAll(async () => {
77+
await s3.deleteBucket({
78+
Bucket,
79+
});
80+
await waitUntilBucketNotExists(
81+
{
82+
client: s3,
83+
maxWaitTime: 60,
84+
},
85+
{
86+
Bucket,
87+
}
88+
);
89+
});
90+
it("should create and verify bucket in us-west-2", async () => {
91+
await s3.createBucket({
92+
Bucket,
93+
});
94+
await waitUntilBucketExists(
95+
{
96+
client: s3,
97+
maxWaitTime: 60,
98+
},
99+
{
100+
Bucket,
101+
}
102+
);
103+
await s3.headBucket({ Bucket });
104+
});
105+
});
106+
107+
describe("Bucket CORS", () => {
108+
let corsBucket: string;
109+
110+
beforeAll(async () => {
111+
corsBucket = getBucketName("cors");
112+
});
113+
114+
afterAll(async () => {
115+
await s3.deleteBucket({
116+
Bucket: corsBucket,
117+
});
118+
});
119+
120+
it("should configure and verify CORS settings", async () => {
121+
await s3.createBucket({
122+
Bucket: corsBucket,
123+
});
124+
await waitUntilBucketExists(
125+
{
126+
client: s3,
127+
maxWaitTime: 60,
128+
},
129+
{
130+
Bucket: corsBucket,
131+
}
132+
);
133+
134+
await s3.putBucketCors({
135+
Bucket: corsBucket,
136+
CORSConfiguration: {
137+
CORSRules: [
138+
{
139+
AllowedMethods: ["DELETE", "POST", "PUT"],
140+
AllowedOrigins: ["http://example.com"],
141+
AllowedHeaders: ["*"],
142+
ExposeHeaders: ["x-amz-server-side-encryption"],
143+
MaxAgeSeconds: 5000,
144+
},
145+
],
146+
},
147+
});
148+
const getBucketCors = await s3.getBucketCors({
149+
Bucket: corsBucket,
150+
});
151+
const corsConfig = getBucketCors.CORSRules?.[0];
152+
153+
expect(corsConfig?.AllowedMethods).toContain("DELETE");
154+
expect(corsConfig?.AllowedMethods).toContain("POST");
155+
expect(corsConfig?.AllowedMethods).toContain("PUT");
156+
expect(corsConfig?.AllowedOrigins?.[0]).toBe("http://example.com");
157+
expect(corsConfig?.AllowedHeaders?.[0]).toBe("*");
158+
expect(corsConfig?.ExposeHeaders?.[0]).toBe("x-amz-server-side-encryption");
159+
expect(corsConfig?.MaxAgeSeconds).toBe(5000);
160+
});
161+
});
162+
163+
describe("Bucket lifecycles", () => {
164+
let lifecycleBucket: string;
165+
166+
beforeAll(async () => {
167+
lifecycleBucket = getBucketName("lifecyc");
168+
});
169+
170+
afterAll(async () => {
171+
await s3.deleteBucket({
172+
Bucket: lifecycleBucket,
173+
});
174+
});
175+
176+
it("should configure and verify lifecycle rules", async () => {
177+
await s3.createBucket({
178+
Bucket: lifecycleBucket,
179+
});
180+
await waitUntilBucketExists(
181+
{
182+
client: s3,
183+
maxWaitTime: 60,
184+
},
185+
{
186+
Bucket: lifecycleBucket,
187+
}
188+
);
189+
await s3.putBucketLifecycleConfiguration({
190+
Bucket: lifecycleBucket,
191+
LifecycleConfiguration: {
192+
Rules: [
193+
{
194+
Filter: {
195+
Prefix: "/",
196+
},
197+
Status: "Enabled",
198+
Transitions: [
199+
{
200+
Days: 0,
201+
StorageClass: "GLACIER",
202+
},
203+
],
204+
},
205+
],
206+
},
207+
});
208+
const lcConfig = await s3.getBucketLifecycleConfiguration({
209+
Bucket: lifecycleBucket,
210+
});
211+
212+
expect(lcConfig?.Rules?.[0]?.Transitions?.[0]?.Days).toBe(0);
213+
expect(lcConfig?.Rules?.[0]?.Transitions?.[0]?.StorageClass).toBe("GLACIER");
214+
});
215+
});
216+
217+
describe("Bucket Tagging", () => {
218+
let taggingBucket: string;
219+
220+
beforeAll(async () => {
221+
taggingBucket = getBucketName("tagging");
222+
});
223+
224+
afterAll(async () => {
225+
await s3.deleteBucket({
226+
Bucket: taggingBucket,
227+
});
228+
});
229+
230+
it("should set and verify bucket tags", async () => {
231+
await s3.createBucket({
232+
Bucket: taggingBucket,
233+
});
234+
await s3.putBucketTagging({
235+
Bucket: taggingBucket,
236+
Tagging: {
237+
TagSet: [
238+
{
239+
Key: "KEY",
240+
Value: "VALUE",
241+
},
242+
],
243+
},
244+
});
245+
const tags = await s3.getBucketTagging({
246+
Bucket: taggingBucket,
247+
});
248+
249+
expect(tags.TagSet?.[0]).toEqual({
250+
Key: "KEY",
251+
Value: "VALUE",
252+
});
253+
});
254+
});
255+
256+
describe("Access bucket following 307 redirects", () => {
257+
let locationConstrained: string;
258+
259+
beforeAll(async () => {
260+
locationConstrained = getBucketName("loc-con", "eu-west-1");
261+
});
262+
263+
afterAll(async () => {
264+
await s3East.deleteBucket({
265+
Bucket: locationConstrained,
266+
});
267+
});
268+
269+
it("should handle bucket creation with location constraint", async () => {
270+
await s3East.createBucket({
271+
Bucket: locationConstrained,
272+
CreateBucketConfiguration: {
273+
LocationConstraint: "eu-west-1" as const,
274+
},
275+
});
276+
277+
await waitUntilBucketExists(
278+
{ client: s3East, maxWaitTime: 60 },
279+
{
280+
Bucket: locationConstrained,
281+
}
282+
);
283+
284+
const headBucket = await s3East.headBucket({
285+
Bucket: locationConstrained,
286+
});
287+
expect(headBucket.BucketRegion).toEqual("eu-west-1");
288+
289+
const bucketLocation = await s3.getBucketLocation({
290+
Bucket: locationConstrained,
291+
});
292+
expect(bucketLocation.LocationConstraint).toEqual("eu-west-1");
293+
});
294+
});
295+
296+
describe("Working with bucket names containing dots", () => {
297+
let dottedName: string;
298+
299+
beforeAll(async () => {
300+
dottedName = getBucketName("x.y.z");
301+
});
302+
303+
afterAll(async () => {
304+
await s3.deleteBucket({
305+
Bucket: dottedName,
306+
});
307+
});
308+
309+
it("should create bucket with DNS compatible dotted name", async () => {
310+
await s3.createBucket({
311+
Bucket: dottedName,
312+
});
313+
await waitUntilBucketExists(
314+
{ client: s3, maxWaitTime: 60 },
315+
{
316+
Bucket: dottedName,
317+
}
318+
);
319+
});
320+
});
321+
322+
describe("Operating on bucket using path style", () => {
323+
let pathStyle: string;
324+
325+
beforeAll(async () => {
326+
pathStyle = getBucketName("path-style");
327+
s3PathStyle.middlewareStack.add(
328+
(next) => async (args) => {
329+
const request = args.request as HttpRequest;
330+
expect(request.path).toContain(pathStyle);
331+
expect(request.hostname).not.toContain(pathStyle);
332+
return next(args);
333+
},
334+
{
335+
step: "finalizeRequest",
336+
override: true,
337+
name: "assertionMiddleware",
338+
}
339+
);
340+
});
341+
342+
afterAll(async () => {
343+
await s3PathStyle.deleteBucket({
344+
Bucket: pathStyle,
345+
});
346+
});
347+
348+
it("should use path style addressing", async () => {
349+
await s3PathStyle.createBucket({
350+
Bucket: pathStyle,
351+
});
352+
353+
await s3PathStyle.putObject({
354+
Bucket: pathStyle,
355+
Key: "hello",
356+
Body: "abc",
357+
});
358+
359+
await s3PathStyle.deleteObject({
360+
Bucket: pathStyle,
361+
Key: "hello",
362+
});
363+
364+
expect.assertions(6);
365+
});
366+
});
367+
}, 60_000);

0 commit comments

Comments
 (0)