diff --git a/clients/client-s3/test/e2e/S3.browser.e2e.spec.ts b/clients/client-s3/test/e2e/S3.browser.e2e.spec.ts index 2e64bb7ceb2f6..b97f7107909e2 100644 --- a/clients/client-s3/test/e2e/S3.browser.e2e.spec.ts +++ b/clients/client-s3/test/e2e/S3.browser.e2e.spec.ts @@ -1,11 +1,11 @@ import type { S3, SelectObjectContentEventStream } from "@aws-sdk/client-s3"; import { fromNodeProviderChain } from "@aws-sdk/credential-providers"; import { FetchHttpHandler } from "@smithy/fetch-http-handler"; -import { afterEach, beforeAll, beforeEach, describe, expect, test as it } from "vitest"; +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, onTestFailed, test as it } from "vitest"; import { getIntegTestResources } from "../../../../tests/e2e/get-integ-test-resources"; import { getRuntimeConfig } from "../../src/runtimeConfig.browser"; -import { S3 as S3Impl } from "../browser-build/browser-s3-bundle"; +import { S3 as S3Impl, waitUntilObjectExists } from "../browser-build/browser-s3-bundle"; import { createBuffer } from "./helpers"; describe("@aws-sdk/client-s3", () => { @@ -14,6 +14,11 @@ describe("@aws-sdk/client-s3", () => { let region: string; let mrapArn: string; let Key = `${Date.now()}`; + const errors = [] as any[]; + let testFailed = false; + const setTestFailed = () => { + testFailed = true; + }; beforeAll(async () => { const integTestResourcesEnv = await getIntegTestResources(); @@ -28,10 +33,28 @@ describe("@aws-sdk/client-s3", () => { region, credentials: fromNodeProviderChain(), requestHandler: new FetchHttpHandler(), + logger: { + ...console, + error(entry: any) { + errors.push(entry); + }, + trace() {}, + debug() {}, + info() {}, + }, }) ) as unknown as S3; }); + afterAll(() => { + if (testFailed) { + console.info("Test failed, logging errors collecting during test."); + for (const error of errors) { + console.error(error); + } + } + }); + describe("PutObject", () => { beforeEach(() => { Key = `${Date.now()}`; @@ -47,6 +70,7 @@ describe("@aws-sdk/client-s3", () => { // Caused by: RequestContentLengthMismatchError: Request body length does not match content-length header // only in vitest + happy-dom. it.skip("should succeed with blob body", async () => { + onTestFailed(setTestFailed); const blob = new Blob([buf]); const result = await client.putObject({ Bucket, @@ -58,6 +82,7 @@ describe("@aws-sdk/client-s3", () => { }); it("should succeed with TypedArray body", async () => { + onTestFailed(setTestFailed); const result = await client.putObject({ Bucket, Key, @@ -67,6 +92,7 @@ describe("@aws-sdk/client-s3", () => { }); it("should succeed with ReadableStream body", async () => { + onTestFailed(setTestFailed); const length = 10 * 1000; // 10KB const chunkSize = 10; const readableStream = new ReadableStream({ @@ -102,6 +128,7 @@ describe("@aws-sdk/client-s3", () => { }); it("should succeed with valid body payload", async () => { + onTestFailed(setTestFailed); // prepare the object. const body = createBuffer("1MB"); @@ -133,7 +160,9 @@ describe("@aws-sdk/client-s3", () => { afterEach(async () => { await client.deleteObject({ Bucket, Key }); }); + it("should succeed with valid bucket", async () => { + onTestFailed(setTestFailed); const result = await client.listObjects({ Bucket, }); @@ -142,6 +171,7 @@ describe("@aws-sdk/client-s3", () => { }); it("should throw with invalid bucket", () => { + onTestFailed(setTestFailed); expect(() => client.listObjects({ Bucket: "invalid-bucket" })).rejects.toThrow(); }); }); @@ -150,9 +180,11 @@ describe("@aws-sdk/client-s3", () => { let UploadId: string; let Etag: string; const multipartObjectKey = `${Key}-multipart`; + beforeEach(() => { Key = `${Date.now()}`; }); + afterEach(async () => { if (UploadId) { await client.abortMultipartUpload({ @@ -168,7 +200,9 @@ describe("@aws-sdk/client-s3", () => { }); it("should successfully create, upload list and complete", async () => { - //create multipart upload + onTestFailed(setTestFailed); + + // create multipart upload const createResult = await client.createMultipartUpload({ Bucket, Key: multipartObjectKey, @@ -209,15 +243,23 @@ describe("@aws-sdk/client-s3", () => { expect(completeResult.$metadata.httpStatusCode).toEqual(200); //validate the object is uploaded - const headResult = await client.headObject({ - Bucket, - Key: multipartObjectKey, - }); - expect(headResult.$metadata.httpStatusCode).toEqual(200); + const waiterState = await waitUntilObjectExists( + { + client, + maxWaitTime: 60, + }, + { + Bucket, + Key: multipartObjectKey, + } + ); + expect(waiterState.state).toEqual("SUCCESS"); }); it("should successfully create, abort, and list upload", async () => { - //create multipart upload + onTestFailed(setTestFailed); + + // create multipart upload const createResult = await client.createMultipartUpload({ Bucket, Key: multipartObjectKey, @@ -248,14 +290,18 @@ describe("@aws-sdk/client-s3", () => { jsrocks,13 node4life,22 esfuture,29`; + beforeEach(async () => { Key = `${Date.now()}`; await client.putObject({ Bucket, Key, Body: csvFile }); }); + afterEach(async () => { await client.deleteObject({ Bucket, Key }); }); + it("should succeed", async () => { + onTestFailed(setTestFailed); const { Payload } = await client.selectObjectContent({ Bucket, Key, @@ -287,8 +333,9 @@ esfuture,29`; beforeEach(async () => { Key = `${Date.now()}`; }); - afterEach(async () => {}); + it("should throw for aws-crt no available in browser", async () => { + onTestFailed(setTestFailed); try { await client.listObjects({ Bucket: mrapArn, @@ -299,4 +346,4 @@ esfuture,29`; } }); }); -}); +}, 60_000); diff --git a/packages/middleware-sdk-s3/src/region-redirect-middleware.e2e.spec.ts b/packages/middleware-sdk-s3/src/region-redirect-middleware.e2e.spec.ts index 78ef2589452d4..95d0b44e16ce2 100644 --- a/packages/middleware-sdk-s3/src/region-redirect-middleware.e2e.spec.ts +++ b/packages/middleware-sdk-s3/src/region-redirect-middleware.e2e.spec.ts @@ -1,15 +1,32 @@ -import { S3 } from "@aws-sdk/client-s3"; +import { S3, S3ClientConfig, waitUntilBucketExists, waitUntilBucketNotExists } from "@aws-sdk/client-s3"; import { GetCallerIdentityCommandOutput, STS } from "@aws-sdk/client-sts"; -import { afterAll, beforeAll, describe, expect, test as it } from "vitest"; +import { afterAll, beforeAll, describe, expect, onTestFailed, test as it } from "vitest"; const testValue = "Hello S3 global client!"; describe("S3 Global Client Test", () => { + const errors = [] as any[]; + let testFailed = false; + const setTestFailed = () => { + testFailed = true; + }; + const regionConfigs = [ - { region: "us-east-1", followRegionRedirects: true }, - { region: "eu-west-1", followRegionRedirects: true }, - { region: "us-west-2", followRegionRedirects: true }, - ]; + { region: "us-east-1", followRegionRedirects: true } as S3ClientConfig, + { region: "eu-west-1", followRegionRedirects: true } as S3ClientConfig, + { region: "us-west-2", followRegionRedirects: true } as S3ClientConfig, + ].map((config) => { + config.logger = { + ...console, + trace() {}, + debug() {}, + info() {}, + error(entry: any) { + errors.push(entry); + }, + }; + return config; + }); const s3Clients = regionConfigs.map((config) => new S3(config)); const stsClient = new STS({}); @@ -24,16 +41,33 @@ describe("S3 Global Client Test", () => { beforeAll(async () => { callerID = await stsClient.getCallerIdentity({}); bucketNames = regionConfigs.map((config) => `${callerID.Account}-${randId}-redirect-${config.region}`); - await Promise.all(bucketNames.map((bucketName, index) => deleteBucket(s3Clients[index], bucketName))); - await Promise.all(bucketNames.map((bucketName, index) => s3Clients[index].createBucket({ Bucket: bucketName }))); + await Promise.all( + bucketNames.map(async (bucketName, index) => { + await deleteBucket(s3Clients[index], bucketName); + return waitUntilBucketNotExists({ client: s3Clients[index], maxWaitTime: 60 }, { Bucket: bucketName }); + }) + ); + await Promise.all( + bucketNames.map(async (bucketName, index) => { + await s3Clients[index].createBucket({ Bucket: bucketName }); + return waitUntilBucketExists({ client: s3Clients[index], maxWaitTime: 60 }, { Bucket: bucketName }); + }) + ); await Promise.all(bucketNames.map((bucketName, index) => s3Clients[index].headBucket({ Bucket: bucketName }))); }); afterAll(async () => { await Promise.all(bucketNames.map((bucketName, index) => deleteBucket(s3Clients[index], bucketName))); + if (testFailed) { + console.info("Test failed, logging errors collecting during test."); + for (const error of errors) { + console.error(error); + } + } }); it("Should be able to put objects following region redirect", async () => { + onTestFailed(setTestFailed); // Upload objects to each bucket for (const bucketName of bucketNames) { for (const s3Client of s3Clients) { @@ -44,6 +78,7 @@ describe("S3 Global Client Test", () => { }); it("Should be able to get objects following region redirect", async () => { + onTestFailed(setTestFailed); // Fetch and assert objects for (const bucketName of bucketNames) { for (const s3Client of s3Clients) { @@ -57,6 +92,7 @@ describe("S3 Global Client Test", () => { }); it("Should delete objects following region redirect", async () => { + onTestFailed(setTestFailed); for (const bucketName of bucketNames) { for (const s3Client of s3Clients) { const objKey = `object-from-${await s3Client.config.region()}-client`;