From 079865694dcdcda0d73d55fb8d724290c08124bd Mon Sep 17 00:00:00 2001 From: George Fu Date: Mon, 21 Oct 2024 14:49:13 +0000 Subject: [PATCH] test: convert unit tests to vitest --- .vscode/settings.json | 3 +- clients/client-ec2/jest.config.js | 5 - jest.config.js | 6 +- lib/lib-dynamodb/jest.config.e2e.js | 5 - lib/lib-dynamodb/jest.config.js | 5 - lib/lib-dynamodb/package.json | 6 +- .../DynamoDBDocumentClientCommand.spec.ts | 4 +- .../src/commands/marshallInput.spec.ts | 2 + lib/lib-dynamodb/src/commands/utils.spec.ts | 2 + .../src/test/lib-dynamodb.e2e.spec.ts | 1205 +++++++++-------- lib/lib-dynamodb/vitest.config.e2e.ts | 8 + lib/lib-dynamodb/vitest.config.ts | 9 + lib/lib-storage/jest.config.e2e.js | 5 - lib/lib-storage/jest.config.js | 5 - lib/lib-storage/package.json | 6 +- lib/lib-storage/src/Upload.spec.ts | 581 ++++---- lib/lib-storage/src/Upload.ts | 13 +- .../src/chunks/getChunkUint8Array.spec.ts | 2 + .../src/chunks/getDataReadable.spec.ts | 1 + .../src/chunks/getDataReadableStream.spec.ts | 1 + lib/lib-storage/src/index.spec.ts | 2 + lib/lib-storage/src/lib-storage.e2e.spec.ts | 106 +- lib/lib-storage/vitest.config.e2e.ts | 8 + lib/lib-storage/vitest.config.ts | 9 + package.json | 5 +- packages/body-checksum-browser/jest.config.js | 6 - packages/body-checksum-browser/package.json | 4 +- .../body-checksum-browser/src/index.spec.ts | 5 +- packages/body-checksum-browser/src/index.ts | 11 +- .../body-checksum-browser/vitest.config.ts | 9 + packages/body-checksum-node/jest.config.js | 5 - packages/body-checksum-node/package.json | 3 +- packages/body-checksum-node/src/index.spec.ts | 1 + packages/body-checksum-node/vitest.config.ts | 9 + .../chunked-stream-reader-node/jest.config.js | 5 - .../chunked-stream-reader-node/package.json | 3 +- .../src/index.spec.ts | 15 +- .../vitest.config.ts | 9 + packages/cloudfront-signer/jest.config.js | 6 - packages/cloudfront-signer/package.json | 3 +- packages/cloudfront-signer/src/sign.spec.ts | 1 + packages/cloudfront-signer/vitest.config.ts | 9 + .../request-handlers.integ.spec.ts | 3 +- packages/core/package.json | 4 +- .../jest.config.js | 7 - .../package.json | 3 +- .../src/InMemoryStorage.spec.ts | 2 + .../src/IndexedDbStorage.spec.ts | 4 +- .../src/fromCognitoIdentity.spec.ts | 3 +- .../src/fromCognitoIdentityPool.spec.ts | 17 +- .../src/localStorage-inmemoryStorage.spec.ts | 2 + .../src/localStorage.spec.ts | 2 + .../src/resolveLogins.spec.ts | 2 + .../vitest.config.ts | 9 + .../credential-provider-env/jest.config.js | 5 - packages/credential-provider-env/package.json | 3 +- .../src/fromEnv.spec.ts | 1 + .../credential-provider-env/vitest.config.ts | 9 + .../credential-provider-http/jest.config.js | 5 - .../credential-provider-http/package.json | 3 +- .../src/fromHttp/checkUrl.spec.ts | 1 + .../src/fromHttp/fromHttp.spec.ts | 21 +- .../src/fromHttp/requestHelpers.spec.ts | 1 + .../credential-provider-http/vitest.config.ts | 9 + .../credential-provider-ini/jest.config.js | 5 - packages/credential-provider-ini/package.json | 3 +- .../src/fromIni.spec.ts | 17 +- .../src/resolveAssumeRoleCredentials.spec.ts | 27 +- .../src/resolveCredentialSource.spec.ts | 20 +- .../src/resolveProcessCredentials.spec.ts | 11 +- .../src/resolveProfileData.spec.ts | 43 +- .../src/resolveSsoCredentials.spec.ts | 9 +- .../src/resolveStaticCredentials.spec.ts | 2 + .../src/resolveWebIdentityCredentials.spec.ts | 15 +- .../credential-provider-ini/vitest.config.ts | 9 + .../credential-provider-node/jest.config.js | 5 - .../credential-provider-node/package.json | 5 +- .../src/defaultProvider.spec.ts | 37 +- .../src/remoteProvider.spec.ts | 46 +- .../vitest.config.integ.ts | 8 + .../credential-provider-node/vitest.config.ts | 9 + .../jest.config.js | 5 - .../credential-provider-process/package.json | 3 +- .../src/fromProcess.spec.ts | 17 +- .../getValidatedProcessCredentials.spec.ts | 1 + .../src/resolveProcessCredentials.spec.ts | 15 +- .../vitest.config.ts | 9 + .../credential-provider-sso/jest.config.js | 5 - packages/credential-provider-sso/package.json | 3 +- .../src/fromSSO.spec.ts | 33 +- .../src/isSsoProfile.spec.ts | 2 + .../src/resolveSSOCredentials.spec.ts | 27 +- .../src/validateSsoProfile.spec.ts | 1 + .../credential-provider-sso/vitest.config.ts | 9 + .../jest.config.js | 5 - .../package.json | 3 +- .../src/fromTokenFile.spec.ts | 37 +- .../src/fromWebToken.spec.ts | 11 +- .../vitest.config.ts | 9 + packages/credential-providers/jest.config.js | 5 - packages/credential-providers/package.json | 5 +- .../src/createCredentialChain.spec.ts | 1 + .../src/fromCognitoIdentity.spec.ts | 11 +- .../src/fromCognitoIdentityPool.spec.ts | 11 +- .../credential-providers/src/fromIni.spec.ts | 21 +- .../src/fromNodeProviderChain.spec.ts | 21 +- .../src/fromSSO.integ.spec.ts | 16 +- .../credential-providers/src/fromSSO.spec.ts | 10 +- .../src/fromTemporaryCredentials.spec.ts | 53 +- .../src/fromTokenFile.spec.ts | 13 +- .../src/fromWebToken.spec.ts | 13 +- .../vitest.config.integ.ts | 8 + .../credential-providers/vitest.config.ts | 9 + .../ec2-metadata-service/jest.config.e2e.js | 5 - packages/ec2-metadata-service/jest.config.js | 5 - packages/ec2-metadata-service/package.json | 6 +- .../src/MetadataService.e2e.spec.ts | 5 +- .../ec2-metadata-service/vitest.config.e2e.ts | 8 + .../ec2-metadata-service/vitest.config.ts | 9 + packages/endpoint-cache/jest.config.js | 5 - packages/endpoint-cache/package.json | 3 +- .../endpoint-cache/src/EndpointCache.spec.ts | 27 +- packages/endpoint-cache/vitest.config.ts | 9 + .../eventstream-handler-node/jest.config.js | 5 - .../eventstream-handler-node/package.json | 3 +- .../src/EventSigningStream.spec.ts | 13 +- .../src/EventStreamPayloadHandler.spec.ts | 21 +- .../eventstream-handler-node/vitest.config.ts | 9 + scripts/validation/vitest-validation.js | 34 +- tests/e2e/get-integ-test-resources.js | 10 +- turbo.json | 5 +- vite.workspace.ts | 1 - vitest.config.e2e.ts | 8 + vitest.config.ts | 29 + yarn.lock | 20 +- 135 files changed, 1736 insertions(+), 1391 deletions(-) delete mode 100644 clients/client-ec2/jest.config.js delete mode 100644 lib/lib-dynamodb/jest.config.e2e.js delete mode 100644 lib/lib-dynamodb/jest.config.js create mode 100644 lib/lib-dynamodb/vitest.config.e2e.ts create mode 100644 lib/lib-dynamodb/vitest.config.ts delete mode 100644 lib/lib-storage/jest.config.e2e.js delete mode 100644 lib/lib-storage/jest.config.js create mode 100644 lib/lib-storage/vitest.config.e2e.ts create mode 100644 lib/lib-storage/vitest.config.ts delete mode 100644 packages/body-checksum-browser/jest.config.js create mode 100644 packages/body-checksum-browser/vitest.config.ts delete mode 100644 packages/body-checksum-node/jest.config.js create mode 100644 packages/body-checksum-node/vitest.config.ts delete mode 100644 packages/chunked-stream-reader-node/jest.config.js create mode 100644 packages/chunked-stream-reader-node/vitest.config.ts delete mode 100644 packages/cloudfront-signer/jest.config.js create mode 100644 packages/cloudfront-signer/vitest.config.ts delete mode 100644 packages/credential-provider-cognito-identity/jest.config.js create mode 100644 packages/credential-provider-cognito-identity/vitest.config.ts delete mode 100644 packages/credential-provider-env/jest.config.js create mode 100644 packages/credential-provider-env/vitest.config.ts delete mode 100644 packages/credential-provider-http/jest.config.js create mode 100644 packages/credential-provider-http/vitest.config.ts delete mode 100644 packages/credential-provider-ini/jest.config.js create mode 100644 packages/credential-provider-ini/vitest.config.ts delete mode 100644 packages/credential-provider-node/jest.config.js create mode 100644 packages/credential-provider-node/vitest.config.integ.ts create mode 100644 packages/credential-provider-node/vitest.config.ts delete mode 100644 packages/credential-provider-process/jest.config.js create mode 100644 packages/credential-provider-process/vitest.config.ts delete mode 100644 packages/credential-provider-sso/jest.config.js create mode 100644 packages/credential-provider-sso/vitest.config.ts delete mode 100644 packages/credential-provider-web-identity/jest.config.js create mode 100644 packages/credential-provider-web-identity/vitest.config.ts delete mode 100644 packages/credential-providers/jest.config.js create mode 100644 packages/credential-providers/vitest.config.integ.ts create mode 100644 packages/credential-providers/vitest.config.ts delete mode 100644 packages/ec2-metadata-service/jest.config.e2e.js delete mode 100644 packages/ec2-metadata-service/jest.config.js create mode 100644 packages/ec2-metadata-service/vitest.config.e2e.ts create mode 100644 packages/ec2-metadata-service/vitest.config.ts delete mode 100644 packages/endpoint-cache/jest.config.js create mode 100644 packages/endpoint-cache/vitest.config.ts delete mode 100644 packages/eventstream-handler-node/jest.config.js create mode 100644 packages/eventstream-handler-node/vitest.config.ts delete mode 100644 vite.workspace.ts create mode 100644 vitest.config.e2e.ts create mode 100644 vitest.config.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index f61e40cbd31d..67b515c68994 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,5 +10,6 @@ "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, - "typescript.tsdk": "node_modules/typescript/lib" + "typescript.tsdk": "node_modules/typescript/lib", + "vitest.disableWorkspaceWarning": true } diff --git a/clients/client-ec2/jest.config.js b/clients/client-ec2/jest.config.js deleted file mode 100644 index a8d1c2e49912..000000000000 --- a/clients/client-ec2/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, -}; diff --git a/jest.config.js b/jest.config.js index 4598d989a4ef..adecff3b0823 100644 --- a/jest.config.js +++ b/jest.config.js @@ -4,9 +4,5 @@ * For tests that involve network requests to live services, see jest.config.e2e.js. */ module.exports = { - projects: [ - "/lib/*/jest.config.js", - "/private/*/jest.config.js", - "/packages/*/jest.config.js", - ], + projects: ["/private/*/jest.config.js", "/packages/*/jest.config.js"], }; diff --git a/lib/lib-dynamodb/jest.config.e2e.js b/lib/lib-dynamodb/jest.config.e2e.js deleted file mode 100644 index c3aa6055ef75..000000000000 --- a/lib/lib-dynamodb/jest.config.e2e.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - preset: "ts-jest", - testMatch: ["**/*.e2e.spec.ts"], - bail: true, -}; diff --git a/lib/lib-dynamodb/jest.config.js b/lib/lib-dynamodb/jest.config.js deleted file mode 100644 index a8d1c2e49912..000000000000 --- a/lib/lib-dynamodb/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, -}; diff --git a/lib/lib-dynamodb/package.json b/lib/lib-dynamodb/package.json index f851cf336f4d..d745b67cee19 100644 --- a/lib/lib-dynamodb/package.json +++ b/lib/lib-dynamodb/package.json @@ -14,8 +14,10 @@ "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", "extract:docs": "api-extractor run --local", - "test": "jest", - "test:e2e": "jest --config jest.config.e2e.js" + "test": "vitest run", + "test:e2e": "vitest run -c vitest.config.e2e.ts", + "test:watch": "vitest watch", + "test:e2e:watch": "vitest watch -c vitest.config.e2e.ts" }, "engines": { "node": ">=16.0.0" diff --git a/lib/lib-dynamodb/src/baseCommand/DynamoDBDocumentClientCommand.spec.ts b/lib/lib-dynamodb/src/baseCommand/DynamoDBDocumentClientCommand.spec.ts index d3f8b530d3cc..d097b8eaafb8 100644 --- a/lib/lib-dynamodb/src/baseCommand/DynamoDBDocumentClientCommand.spec.ts +++ b/lib/lib-dynamodb/src/baseCommand/DynamoDBDocumentClientCommand.spec.ts @@ -1,4 +1,5 @@ import { Handler, MiddlewareStack } from "@smithy/types"; +import { describe, expect, test as it } from "vitest"; import { KeyNodeChildren } from "../commands/utils"; import { DynamoDBDocumentClientCommand } from "./DynamoDBDocumentClientCommand"; @@ -34,8 +35,7 @@ class AnyCommand extends DynamoDBDocumentClientCommand<{}, {}, {}, {}, {}> { } describe("DynamoDBDocumentClientCommand", () => { - // ToDo: Investigate why Jest29 throws TypeError: Class constructor Command cannot be invoked without 'new' - it.skip("should not allow usage of the default middlewareStack", () => { + it("should not allow usage of the default middlewareStack", () => { const command = new AnyCommand(); command.resolveMiddleware(null as any, null as any, null as any); { diff --git a/lib/lib-dynamodb/src/commands/marshallInput.spec.ts b/lib/lib-dynamodb/src/commands/marshallInput.spec.ts index f002bad71276..c44f4644770a 100644 --- a/lib/lib-dynamodb/src/commands/marshallInput.spec.ts +++ b/lib/lib-dynamodb/src/commands/marshallInput.spec.ts @@ -1,3 +1,5 @@ +import { describe, expect, test as it } from "vitest"; + import { marshallInput } from "./utils"; describe("marshallInput and processObj", () => { diff --git a/lib/lib-dynamodb/src/commands/utils.spec.ts b/lib/lib-dynamodb/src/commands/utils.spec.ts index d2d044900070..acb17680efdb 100644 --- a/lib/lib-dynamodb/src/commands/utils.spec.ts +++ b/lib/lib-dynamodb/src/commands/utils.spec.ts @@ -1,3 +1,5 @@ +import { describe, expect, test as it } from "vitest"; + import { marshallInput, unmarshallOutput } from "./utils"; describe("utils", () => { diff --git a/lib/lib-dynamodb/src/test/lib-dynamodb.e2e.spec.ts b/lib/lib-dynamodb/src/test/lib-dynamodb.e2e.spec.ts index c91aa8a2e5ad..57b5e7a94738 100644 --- a/lib/lib-dynamodb/src/test/lib-dynamodb.e2e.spec.ts +++ b/lib/lib-dynamodb/src/test/lib-dynamodb.e2e.spec.ts @@ -23,9 +23,9 @@ import { TransactWriteCommandOutput, UpdateCommandOutput, } from "@aws-sdk/lib-dynamodb"; +import { afterAll, beforeAll, describe, expect, test as it, vi } from "vitest"; // expected running time: table creation (~20s) + operations 10s -jest.setTimeout(180000); describe(DynamoDBDocument.name, () => { it("should deny initialization with cacheMiddleware: true", () => { @@ -39,705 +39,718 @@ describe(DynamoDBDocument.name, () => { }); }); -describe(DynamoDBDocument.name, () => { - type NestedList = (string | NumberValue | boolean | Set | Set | null | NestedList | NestedMap)[]; - type NestedMap = { - [key: string]: string | NumberValue | boolean | Set | Set | null | NestedList | NestedMap; - }; - - type DataType = { - null: null; - string: string; - number: NumberValue; - bigInt: NumberValue; - bigNumber: NumberValue; - boolean: boolean; - sSet: Set; - nSet: Set; - list: NestedList; - map: NestedMap; - [key: string]: any; - }; - - const dynamodb = new DynamoDB({ region: "us-west-2", maxAttempts: 10 }); - const doc = DynamoDBDocument.from(dynamodb, { - marshallOptions: { - convertTopLevelContainer: true, - convertClassInstanceToMap: true, - }, - unmarshallOptions: { - wrapNumbers: true, - }, - }); +describe( + DynamoDBDocument.name, + () => { + type NestedList = ( + | string + | NumberValue + | boolean + | Set + | Set + | null + | NestedList + | NestedMap + )[]; + type NestedMap = { + [key: string]: string | NumberValue | boolean | Set | Set | null | NestedList | NestedMap; + }; + + type DataType = { + null: null; + string: string; + number: NumberValue; + bigInt: NumberValue; + bigNumber: NumberValue; + boolean: boolean; + sSet: Set; + nSet: Set; + list: NestedList; + map: NestedMap; + [key: string]: any; + }; + + const dynamodb = new DynamoDB({ region: "us-west-2", maxAttempts: 10 }); + const doc = DynamoDBDocument.from(dynamodb, { + marshallOptions: { + convertTopLevelContainer: true, + convertClassInstanceToMap: true, + }, + unmarshallOptions: { + wrapNumbers: true, + }, + }); - function throwIfError(e: unknown) { - if (e instanceof Error) { - throw e; + function throwIfError(e: unknown) { + if (e instanceof Error) { + throw e; + } } - } - - // Tables will be dropped at the end of the test. - // For faster test development, remove this random suffix and - // don't delete the table in afterAll(). - // The table will in that case be re-used. - const randId = (Math.random() + 1).toString(36).substring(2, 6); - const timestamp = (Date.now() / 1000) | 0; - - const TableName = `js-sdk-dynamodb-test-${timestamp}-${randId}`; - - const log = { - describe: null as null | DescribeTableCommandOutput, - create: null as null | CreateTableCommandOutput, - write: {} as Record, - read: {} as Record, - undefinedColumnWrite: null as null | PutCommandOutput, - undefinedColumnRead: null as null | GetCommandOutput, - batchWrite: null as null | BatchWriteCommandOutput, - batchRead: null as null | BatchGetCommandOutput, - transactWrite: null as null | TransactWriteCommandOutput, - transactRead: null as null | TransactGetCommandOutput, - executeTransaction: null as null | ExecuteTransactionCommandOutput, - executeTransactionReadBack: {} as Record, - executeStatement: {} as Record, - executeStatementReadBack: {} as Record, - batchExecuteStatement: null as null | BatchExecuteStatementCommandOutput, - batchExecuteStatementReadBack: null as null | BatchExecuteStatementCommandOutput, - query: null as null | QueryCommandOutput, - scan: null as null | ScanCommandOutput, - update: {} as Record, - updateReadBack: {} as Record, - delete: {} as Record, - classInstanceConversion: { - write: null as null | PutCommandOutput, - read: null as null | GetCommandOutput, - }, - }; - - const data: DataType = { - null: null, - string: "myString", - number: NumberValue.from(1), - bigInt: NumberValue.from( - "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - ), - bigNumber: NumberValue.from("3210000000000000000.0000000000000123"), - boolean: true, - sSet: new Set(["my", "string", "set"]), - nSet: new Set([2, 3, 4].map(NumberValue.from)), - list: [ - null, - "myString", - NumberValue.from(1), - true, - new Set(["my", "string", "set"]), - new Set([NumberValue.from(2), NumberValue.from(3), NumberValue.from(4)]), - new Set([ - NumberValue.from("3210000000000000000.0000000000000123"), - NumberValue.from("3210000000000000001.0000000000000123"), - NumberValue.from("3210000000000000002.0000000000000123"), - ]), - ["listInList", NumberValue.from(1), null], - { - mapInList: "mapInList", + + // Tables will be dropped at the end of the test. + // For faster test development, remove this random suffix and + // don't delete the table in afterAll(). + // The table will in that case be re-used. + const randId = (Math.random() + 1).toString(36).substring(2, 6); + const timestamp = (Date.now() / 1000) | 0; + + const TableName = `js-sdk-dynamodb-test-${timestamp}-${randId}`; + + const log = { + describe: null as null | DescribeTableCommandOutput, + create: null as null | CreateTableCommandOutput, + write: {} as Record, + read: {} as Record, + undefinedColumnWrite: null as null | PutCommandOutput, + undefinedColumnRead: null as null | GetCommandOutput, + batchWrite: null as null | BatchWriteCommandOutput, + batchRead: null as null | BatchGetCommandOutput, + transactWrite: null as null | TransactWriteCommandOutput, + transactRead: null as null | TransactGetCommandOutput, + executeTransaction: null as null | ExecuteTransactionCommandOutput, + executeTransactionReadBack: {} as Record, + executeStatement: {} as Record, + executeStatementReadBack: {} as Record, + batchExecuteStatement: null as null | BatchExecuteStatementCommandOutput, + batchExecuteStatementReadBack: null as null | BatchExecuteStatementCommandOutput, + query: null as null | QueryCommandOutput, + scan: null as null | ScanCommandOutput, + update: {} as Record, + updateReadBack: {} as Record, + delete: {} as Record, + classInstanceConversion: { + write: null as null | PutCommandOutput, + read: null as null | GetCommandOutput, }, - ], - map: { + }; + + const data: DataType = { null: null, string: "myString", number: NumberValue.from(1), + bigInt: NumberValue.from( + "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ), + bigNumber: NumberValue.from("3210000000000000000.0000000000000123"), boolean: true, sSet: new Set(["my", "string", "set"]), nSet: new Set([2, 3, 4].map(NumberValue.from)), - listInMap: ["listInMap", NumberValue.from(1), null], - mapInMap: { mapInMap: "mapInMap" }, - }, - }; - - const updateTransform = (input: T): T => { - switch (typeof input) { - case "object": - if (input === null) { - return null as T; - } - if (Array.isArray(input)) { - return input.map(updateTransform) as T; - } - if (input instanceof Set) { - return new Set([...input].map(updateTransform)) as T; - } - if (input instanceof NumberValue) { - return NumberValue.from(input.toString()) as T; - } - return Object.entries(input).reduce((acc: { [key: string]: any }, [k, v]) => { - acc[updateTransform(k)] = updateTransform(v); - return acc; - }, {}) as T; - case "boolean": - return !input as T; - case "number": - return (input + 1) as T; - case "string": - return (input + "-x") as T; - } - return input; - }; - - const passError = (e: any) => e; - - beforeAll(async () => { - log.describe = await dynamodb - .describeTable({ - TableName, - }) - .catch((e) => { - return null; - }); - if (!log.describe?.Table) { - log.create = await dynamodb - .createTable({ + list: [ + null, + "myString", + NumberValue.from(1), + true, + new Set(["my", "string", "set"]), + new Set([NumberValue.from(2), NumberValue.from(3), NumberValue.from(4)]), + new Set([ + NumberValue.from("3210000000000000000.0000000000000123"), + NumberValue.from("3210000000000000001.0000000000000123"), + NumberValue.from("3210000000000000002.0000000000000123"), + ]), + ["listInList", NumberValue.from(1), null], + { + mapInList: "mapInList", + }, + ], + map: { + null: null, + string: "myString", + number: NumberValue.from(1), + boolean: true, + sSet: new Set(["my", "string", "set"]), + nSet: new Set([2, 3, 4].map(NumberValue.from)), + listInMap: ["listInMap", NumberValue.from(1), null], + mapInMap: { mapInMap: "mapInMap" }, + }, + }; + + const updateTransform = (input: T): T => { + switch (typeof input) { + case "object": + if (input === null) { + return null as T; + } + if (Array.isArray(input)) { + return input.map(updateTransform) as T; + } + if (input instanceof Set) { + return new Set([...input].map(updateTransform)) as T; + } + if (input instanceof NumberValue) { + return NumberValue.from(input.toString()) as T; + } + return Object.entries(input).reduce((acc: { [key: string]: any }, [k, v]) => { + acc[updateTransform(k)] = updateTransform(v); + return acc; + }, {}) as T; + case "boolean": + return !input as T; + case "number": + return (input + 1) as T; + case "string": + return (input + "-x") as T; + } + return input; + }; + + const passError = (e: any) => e; + + beforeAll(async () => { + log.describe = await dynamodb + .describeTable({ TableName, - AttributeDefinitions: [ - { - AttributeName: "id", - AttributeType: "S", + }) + .catch((e) => { + return null; + }); + if (!log.describe?.Table) { + log.create = await dynamodb + .createTable({ + TableName, + AttributeDefinitions: [ + { + AttributeName: "id", + AttributeType: "S", + }, + ], + KeySchema: [ + { + AttributeName: "id", + KeyType: "HASH", + }, + ], + BillingMode: BillingMode.PAY_PER_REQUEST, + }) + .catch(passError); + await waitUntilTableExists( + { client: dynamodb, maxWaitTime: 120 }, + { + TableName, + } + ); + } + + for (const [id, value] of [["1", data as any], ...Object.entries(data)]) { + log.write[id] = await doc + .put({ + TableName, + Item: { + id, + data: value, }, - ], - KeySchema: [ - { - AttributeName: "id", - KeyType: "HASH", + }) + .catch(passError); + + log.read[id] = await doc + .get({ + ConsistentRead: true, + TableName, + Key: { + id, }, - ], - BillingMode: BillingMode.PAY_PER_REQUEST, - }) - .catch(passError); - await waitUntilTableExists( - { client: dynamodb, maxWaitTime: 120 }, - { - TableName, - } - ); - } - - for (const [id, value] of [["1", data as any], ...Object.entries(data)]) { - log.write[id] = await doc - .put({ - TableName, - Item: { - id, - data: value, + }) + .catch(passError); + } + + log.batchWrite = await doc + .batchWrite({ + RequestItems: { + [TableName]: [ + ...Object.entries(data).map(([k, v]) => { + return { + PutRequest: { + Item: { + id: k + "-batch", + data: v, + }, + }, + }; + }), + ], }, }) .catch(passError); - log.read[id] = await doc - .get({ - ConsistentRead: true, - TableName, - Key: { - id, + log.batchRead = await doc + .batchGet({ + RequestItems: { + [TableName]: { + Keys: [ + ...Object.keys(data).map((key) => { + return { id: key + "-batch" }; + }), + ], + }, }, }) .catch(passError); - } - log.batchWrite = await doc - .batchWrite({ - RequestItems: { - [TableName]: [ + log.transactWrite = await doc + .transactWrite({ + TransactItems: [ ...Object.entries(data).map(([k, v]) => { return { - PutRequest: { + Put: { + TableName, + Key: { + id: k + "-transact", + }, Item: { - id: k + "-batch", + id: k + "-transact", data: v, }, }, }; }), ], - }, - }) - .catch(passError); - - log.batchRead = await doc - .batchGet({ - RequestItems: { - [TableName]: { - Keys: [ - ...Object.keys(data).map((key) => { - return { id: key + "-batch" }; - }), - ], - }, - }, - }) - .catch(passError); - - log.transactWrite = await doc - .transactWrite({ - TransactItems: [ - ...Object.entries(data).map(([k, v]) => { - return { - Put: { - TableName, - Key: { - id: k + "-transact", - }, - Item: { - id: k + "-transact", - data: v, - }, - }, - }; - }), - ], - }) - .catch(passError); - - log.transactRead = await doc - .transactGet({ - TransactItems: [ - ...Object.keys(data).map((k) => { - return { - Get: { - TableName, - Key: { - id: k + "-transact", + }) + .catch(passError); + + log.transactRead = await doc + .transactGet({ + TransactItems: [ + ...Object.keys(data).map((k) => { + return { + Get: { + TableName, + Key: { + id: k + "-transact", + }, }, - }, - }; - }), - ], - }) - .catch(passError); - - log.executeTransaction = await doc - .executeTransaction({ - TransactStatements: [ - ...Object.entries(data).map(([k, v]) => { - return { - Statement: `INSERT INTO "${TableName}" value {'id':?,'data':?}`, - Parameters: [k + "-exec-transact", v], - }; - }), - ], - }) - .catch(passError); - for (const [k] of Object.entries(data)) { - log.executeTransactionReadBack[k] = await doc - .get({ + }; + }), + ], + }) + .catch(passError); + + log.executeTransaction = await doc + .executeTransaction({ + TransactStatements: [ + ...Object.entries(data).map(([k, v]) => { + return { + Statement: `INSERT INTO "${TableName}" value {'id':?,'data':?}`, + Parameters: [k + "-exec-transact", v], + }; + }), + ], + }) + .catch(passError); + for (const [k] of Object.entries(data)) { + log.executeTransactionReadBack[k] = await doc + .get({ + ConsistentRead: true, + TableName, + Key: { + id: k + "-exec-transact", + }, + }) + .catch(passError); + } + + for (const [k, v] of Object.entries(data)) { + log.executeStatement[k] = await doc + .executeStatement({ + Statement: `INSERT INTO "${TableName}" value {'id':?,'data':?}`, + Parameters: [k + "-statement", v], + }) + .catch(passError); + } + for (const [k] of Object.entries(data)) { + log.executeStatementReadBack[k] = await doc + .get({ + ConsistentRead: true, + TableName, + Key: { + id: k + "-statement", + }, + }) + .catch(passError); + } + + log.batchExecuteStatement = await doc + .batchExecuteStatement({ + Statements: [ + ...Object.entries(data).map(([k, v]) => { + return { + Statement: `INSERT INTO "${TableName}" value {'id':?,'data':?}`, + Parameters: [k + "-batch-statement", v], + }; + }), + ], + }) + .catch(passError); + + log.batchExecuteStatementReadBack = await doc + .batchExecuteStatement({ + Statements: [ + ...Object.entries(data).map(([k, v]) => { + return { + Statement: `SELECT * FROM ${TableName} WHERE "id" = ?`, + Parameters: [k + "-batch-statement"], + }; + }), + ], + }) + .catch(passError); + + log.query = await doc + .query({ + TableName, + KeyConditionExpression: `id = :id`, + ExpressionAttributeValues: { + ":id": "map", + }, ConsistentRead: true, + }) + .catch(passError); + + log.scan = await doc + .scan({ TableName, - Key: { - id: k + "-exec-transact", + FilterExpression: `#data = :data1 OR #data = :data2`, + ExpressionAttributeNames: { + "#data": "data", + }, + ExpressionAttributeValues: { + ":data1": data.list, + ":data2": data.map, }, + ConsistentRead: true, }) .catch(passError); - } - for (const [k, v] of Object.entries(data)) { - log.executeStatement[k] = await doc - .executeStatement({ - Statement: `INSERT INTO "${TableName}" value {'id':?,'data':?}`, - Parameters: [k + "-statement", v], + log.undefinedColumnWrite = await doc + .put({ + TableName, + Item: { + id: "undefinedColumns", + A: "A", + B: undefined, + C: "C", + D: undefined, + E: "E", + }, }) .catch(passError); - } - for (const [k] of Object.entries(data)) { - log.executeStatementReadBack[k] = await doc + + log.undefinedColumnRead = await doc .get({ - ConsistentRead: true, TableName, Key: { - id: k + "-statement", + id: "undefinedColumns", }, + ConsistentRead: true, }) .catch(passError); - } - - log.batchExecuteStatement = await doc - .batchExecuteStatement({ - Statements: [ - ...Object.entries(data).map(([k, v]) => { - return { - Statement: `INSERT INTO "${TableName}" value {'id':?,'data':?}`, - Parameters: [k + "-batch-statement", v], - }; - }), - ], - }) - .catch(passError); - - log.batchExecuteStatementReadBack = await doc - .batchExecuteStatement({ - Statements: [ - ...Object.entries(data).map(([k, v]) => { - return { - Statement: `SELECT * FROM ${TableName} WHERE "id" = ?`, - Parameters: [k + "-batch-statement"], - }; - }), - ], - }) - .catch(passError); - - log.query = await doc - .query({ - TableName, - KeyConditionExpression: `id = :id`, - ExpressionAttributeValues: { - ":id": "map", - }, - ConsistentRead: true, - }) - .catch(passError); - log.scan = await doc - .scan({ - TableName, - FilterExpression: `#data = :data1 OR #data = :data2`, - ExpressionAttributeNames: { - "#data": "data", - }, - ExpressionAttributeValues: { - ":data1": data.list, - ":data2": data.map, - }, - ConsistentRead: true, - }) - .catch(passError); - - log.undefinedColumnWrite = await doc - .put({ - TableName, - Item: { - id: "undefinedColumns", - A: "A", - B: undefined, - C: "C", - D: undefined, - E: "E", - }, - }) - .catch(passError); - - log.undefinedColumnRead = await doc - .get({ - TableName, - Key: { - id: "undefinedColumns", - }, - ConsistentRead: true, - }) - .catch(passError); + for (const [id, value] of [["1", data as any], ...Object.entries(data)]) { + log.update[id] = await doc + .update({ + TableName, + Key: { + id, + }, + AttributeUpdates: { + data: { + Action: "PUT", + Value: updateTransform(value), + }, + }, + }) + .catch(passError); + + log.updateReadBack[id] = await doc + .get({ + ConsistentRead: true, + TableName, + Key: { + id, + }, + }) + .catch(passError); - for (const [id, value] of [["1", data as any], ...Object.entries(data)]) { - log.update[id] = await doc - .update({ + log.delete[id] = await (async () => { + for (const suffix of ["-batch", "-transact", "-exec-transact", "-statement", "-batch-statement"]) { + doc + .delete({ + TableName, + Key: { + id: id + suffix, + }, + }) + .catch(() => {}); + } + return doc.delete({ + TableName, + Key: { id }, + }); + })().catch(passError); + } + + log.classInstanceConversion.write = await doc + .put({ TableName, - Key: { - id, - }, - AttributeUpdates: { + Item: { + id: "classInstance", data: { - Action: "PUT", - Value: updateTransform(value), + a: new (class { + public a = 1; + public b = 2; + public c = 3; + public method() { + return "method"; + } + public get getter() { + return "getter"; + } + public arrowFn = () => "arrowFn"; + public ownFunction = function () { + return "ownFunction"; + }; + })(), + b: new (class { + public a = 4; + public b = 5; + public c = 6; + public method() { + return "method"; + } + public get getter() { + return "getter"; + } + public arrowFn = () => "arrowFn"; + public ownFunction = function () { + return "ownFunction"; + }; + })(), }, }, }) .catch(passError); - log.updateReadBack[id] = await doc + log.classInstanceConversion.read = await doc .get({ ConsistentRead: true, TableName, Key: { - id, + id: "classInstance", }, }) .catch(passError); + }, 180_000); - log.delete[id] = await (async () => { - for (const suffix of ["-batch", "-transact", "-exec-transact", "-statement", "-batch-statement"]) { - doc - .delete({ - TableName, - Key: { - id: id + suffix, - }, - }) - .catch(() => {}); - } - return doc.delete({ - TableName, - Key: { id }, - }); - })().catch(passError); - } - - log.classInstanceConversion.write = await doc - .put({ + afterAll(async () => { + await dynamodb.deleteTable({ TableName, - Item: { - id: "classInstance", - data: { - a: new (class { - public a = 1; - public b = 2; - public c = 3; - public method() { - return "method"; - } - public get getter() { - return "getter"; - } - public arrowFn = () => "arrowFn"; - public ownFunction = function () { - return "ownFunction"; - }; - })(), - b: new (class { - public a = 4; - public b = 5; - public c = 6; - public method() { - return "method"; - } - public get getter() { - return "getter"; - } - public arrowFn = () => "arrowFn"; - public ownFunction = function () { - return "ownFunction"; - }; - })(), - }, - }, - }) - .catch(passError); - - log.classInstanceConversion.read = await doc - .get({ - ConsistentRead: true, - TableName, - Key: { - id: "classInstance", - }, - }) - .catch(passError); - }); - - afterAll(async () => { - await dynamodb.deleteTable({ - TableName, + }); }); - }); - describe("updateTransformFunction", () => { - it("modifies all fields of an object", () => { - expect(updateTransform(data)).toEqual({ - "null-x": null, - "string-x": "myString-x", - "number-x": NumberValue.from(1), - "bigInt-x": NumberValue.from( - "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - ), - "bigNumber-x": NumberValue.from("3210000000000000000.0000000000000123"), - "boolean-x": false, - "sSet-x": new Set(["my-x", "string-x", "set-x"]), - "nSet-x": new Set([2, 3, 4].map(NumberValue.from)), - "list-x": [ - null, - "myString-x", - NumberValue.from(1), - false, - new Set(["my-x", "string-x", "set-x"]), - new Set([2, 3, 4].map(NumberValue.from)), - new Set([ - NumberValue.from("3210000000000000000.0000000000000123"), - NumberValue.from("3210000000000000001.0000000000000123"), - NumberValue.from("3210000000000000002.0000000000000123"), - ]), - ["listInList-x", NumberValue.from(1), null], - { "mapInList-x": "mapInList-x" }, - ], - "map-x": { + describe("updateTransformFunction", () => { + it("modifies all fields of an object", () => { + expect(updateTransform(data)).toEqual({ "null-x": null, "string-x": "myString-x", "number-x": NumberValue.from(1), + "bigInt-x": NumberValue.from( + "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ), + "bigNumber-x": NumberValue.from("3210000000000000000.0000000000000123"), "boolean-x": false, "sSet-x": new Set(["my-x", "string-x", "set-x"]), "nSet-x": new Set([2, 3, 4].map(NumberValue.from)), - "listInMap-x": ["listInMap-x", NumberValue.from(1), null], - "mapInMap-x": { "mapInMap-x": "mapInMap-x" }, - }, + "list-x": [ + null, + "myString-x", + NumberValue.from(1), + false, + new Set(["my-x", "string-x", "set-x"]), + new Set([2, 3, 4].map(NumberValue.from)), + new Set([ + NumberValue.from("3210000000000000000.0000000000000123"), + NumberValue.from("3210000000000000001.0000000000000123"), + NumberValue.from("3210000000000000002.0000000000000123"), + ]), + ["listInList-x", NumberValue.from(1), null], + { "mapInList-x": "mapInList-x" }, + ], + "map-x": { + "null-x": null, + "string-x": "myString-x", + "number-x": NumberValue.from(1), + "boolean-x": false, + "sSet-x": new Set(["my-x", "string-x", "set-x"]), + "nSet-x": new Set([2, 3, 4].map(NumberValue.from)), + "listInMap-x": ["listInMap-x", NumberValue.from(1), null], + "mapInMap-x": { "mapInMap-x": "mapInMap-x" }, + }, + }); }); }); - }); - it("initializes using the static constructor", async () => { - expect(doc).toBeInstanceOf(DynamoDBDocument); - }); + it("initializes using the static constructor", async () => { + expect(doc).toBeInstanceOf(DynamoDBDocument); + }); - it(`is using a random TableName=${TableName}`, async () => { - // to report the table name - }); + it(`is using a random TableName=${TableName}`, async () => { + // to report the table name + }); - it("describes the test table", async () => { - if (log.describe) { - expect(log.describe?.Table?.TableName).toEqual(TableName); - } - }); + it("describes the test table", async () => { + if (log.describe) { + expect(log.describe?.Table?.TableName).toEqual(TableName); + } + }); - it("creates the test table if it does not exist", async () => { - if (log.describe) { - throwIfError(log.describe); - expect(log.describe?.Table?.TableName).toEqual(TableName); - } else { - throwIfError(log.create); - expect(log.create?.TableDescription?.TableName).toEqual(TableName); - } - }); + it("creates the test table if it does not exist", async () => { + if (log.describe) { + throwIfError(log.describe); + expect(log.describe?.Table?.TableName).toEqual(TableName); + } else { + throwIfError(log.create); + expect(log.create?.TableDescription?.TableName).toEqual(TableName); + } + }); - it("ignores undefined column values for backwards compatibility", async () => { - throwIfError(log.undefinedColumnWrite); + it("ignores undefined column values for backwards compatibility", async () => { + throwIfError(log.undefinedColumnWrite); - expect(log.undefinedColumnRead?.Item).toEqual({ - id: "undefinedColumns", - A: "A", - C: "C", - E: "E", + expect(log.undefinedColumnRead?.Item).toEqual({ + id: "undefinedColumns", + A: "A", + C: "C", + E: "E", + }); }); - }); - - it("can batch write", async () => { - throwIfError(log.batchWrite); - }); - it("can batch read", async () => { - throwIfError(log.batchRead); - const results = log.batchRead?.Responses?.[TableName] ?? []; + it("can batch write", async () => { + throwIfError(log.batchWrite); + }); - for (const result of results) { - expect(result.data).toEqual(data[result.id.replace("-batch", "")]); - } - }); + it("can batch read", async () => { + throwIfError(log.batchRead); + const results = log.batchRead?.Responses?.[TableName] ?? []; - it("can transact write", async () => { - throwIfError(log.transactWrite); - }); + for (const result of results) { + expect(result.data).toEqual(data[result.id.replace("-batch", "")]); + } + }); - it("can transact read", async () => { - throwIfError(log.transactRead); - const results = log.transactRead?.Responses ?? []; + it("can transact write", async () => { + throwIfError(log.transactWrite); + }); - for (const result of results) { - expect(result.Item?.data).toEqual(data[result.Item?.id.replace("-transact", "")]); - } - }); + it("can transact read", async () => { + throwIfError(log.transactRead); + const results = log.transactRead?.Responses ?? []; - it("can execute transactions", async () => { - throwIfError(log.executeTransaction); - }); + for (const result of results) { + expect(result.Item?.data).toEqual(data[result.Item?.id.replace("-transact", "")]); + } + }); - it("can batch execute statements", async () => { - throwIfError(log.batchExecuteStatement); + it("can execute transactions", async () => { + throwIfError(log.executeTransaction); + }); - expect(log.batchExecuteStatementReadBack?.Responses).toBeInstanceOf(Array); - expect(log.batchExecuteStatementReadBack?.Responses?.length).toBeGreaterThan(0); - for (const response of log.batchExecuteStatementReadBack?.Responses ?? []) { - expect(response.Item?.data).toEqual(data[response.Item?.id?.replace("-batch-statement", "")]); - } - }); + it("can batch execute statements", async () => { + throwIfError(log.batchExecuteStatement); - it("can query", async () => { - throwIfError(log.query); - expect(log.query?.Items).toContainEqual({ - id: "map", - data: data.map, + expect(log.batchExecuteStatementReadBack?.Responses).toBeInstanceOf(Array); + expect(log.batchExecuteStatementReadBack?.Responses?.length).toBeGreaterThan(0); + for (const response of log.batchExecuteStatementReadBack?.Responses ?? []) { + expect(response.Item?.data).toEqual(data[response.Item?.id?.replace("-batch-statement", "")]); + } }); - }); - it("can scan", async () => { - throwIfError(log.scan); - expect(log.scan?.Items).toContainEqual({ - id: "map", - data: data.map, + it("can query", async () => { + throwIfError(log.query); + expect(log.query?.Items).toContainEqual({ + id: "map", + data: data.map, + }); }); - expect(log.scan?.Items).toContainEqual({ - id: "list", - data: data.list, - }); - }); + it("can scan", async () => { + throwIfError(log.scan); + expect(log.scan?.Items).toContainEqual({ + id: "map", + data: data.map, + }); - for (const [key, value] of Object.entries(data)) { - it(`can write data of type ${key}`, async () => { - throwIfError(log.write[key]); - expect(log.write[key].$metadata).toBeDefined(); + expect(log.scan?.Items).toContainEqual({ + id: "list", + data: data.list, + }); }); - it(`can execute statement inserting type ${key}`, async () => { - const match = log.executeStatement[key]; - expect(match).toBeDefined(); + for (const [key, value] of Object.entries(data)) { + it(`can write data of type ${key}`, async () => { + throwIfError(log.write[key]); + expect(log.write[key].$metadata).toBeDefined(); + }); - throwIfError(match); - }); + it(`can execute statement inserting type ${key}`, async () => { + const match = log.executeStatement[key]; + expect(match).toBeDefined(); - it(`can read back data inserted via ExecuteStatement of type ${key}`, async () => { - throwIfError(log.executeStatementReadBack[key]); - expect(log.executeStatementReadBack[key].Item).toEqual({ - id: key + "-statement", - data: value, + throwIfError(match); }); - }); - it(`can read back data inserted via ExecuteTransaction of type ${key}`, async () => { - throwIfError(log.executeTransactionReadBack[key]); - expect(log.executeTransactionReadBack[key].Item).toEqual({ - id: key + "-exec-transact", - data: value, + it(`can read back data inserted via ExecuteStatement of type ${key}`, async () => { + throwIfError(log.executeStatementReadBack[key]); + expect(log.executeStatementReadBack[key].Item).toEqual({ + id: key + "-statement", + data: value, + }); }); - }); - it(`can read data of type ${key}`, async () => { - throwIfError(log.read[key]); - expect(log.read[key].Item).toEqual({ - id: key, - data: value, + it(`can read back data inserted via ExecuteTransaction of type ${key}`, async () => { + throwIfError(log.executeTransactionReadBack[key]); + expect(log.executeTransactionReadBack[key].Item).toEqual({ + id: key + "-exec-transact", + data: value, + }); }); - }); - it(`can update data of type ${key}`, async () => { - throwIfError(log.updateReadBack[key]); - expect(log.updateReadBack[key].Item).toEqual({ - id: key, - data: updateTransform(value), + it(`can read data of type ${key}`, async () => { + throwIfError(log.read[key]); + expect(log.read[key].Item).toEqual({ + id: key, + data: value, + }); }); - }); - it(`can delete data of type ${key}`, async () => { - throwIfError(log.delete[key]); - expect(log.delete[key].$metadata).toBeDefined(); - }); - } - - it("can serialize class instances as maps", async () => { - expect(log.classInstanceConversion.read?.Item).toEqual({ - id: "classInstance", - data: { - a: { - a: NumberValue.from(1), - b: NumberValue.from(2), - c: NumberValue.from(3), - }, - b: { - a: NumberValue.from(4), - b: NumberValue.from(5), - c: NumberValue.from(6), + it(`can update data of type ${key}`, async () => { + throwIfError(log.updateReadBack[key]); + expect(log.updateReadBack[key].Item).toEqual({ + id: key, + data: updateTransform(value), + }); + }); + + it(`can delete data of type ${key}`, async () => { + throwIfError(log.delete[key]); + expect(log.delete[key].$metadata).toBeDefined(); + }); + } + + it("can serialize class instances as maps", async () => { + expect(log.classInstanceConversion.read?.Item).toEqual({ + id: "classInstance", + data: { + a: { + a: NumberValue.from(1), + b: NumberValue.from(2), + c: NumberValue.from(3), + }, + b: { + a: NumberValue.from(4), + b: NumberValue.from(5), + c: NumberValue.from(6), + }, }, - }, + }); }); - }); -}); + }, + 180_000 +); diff --git a/lib/lib-dynamodb/vitest.config.e2e.ts b/lib/lib-dynamodb/vitest.config.e2e.ts new file mode 100644 index 000000000000..8e3e8ccc5726 --- /dev/null +++ b/lib/lib-dynamodb/vitest.config.e2e.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["**/*.e2e.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/lib/lib-dynamodb/vitest.config.ts b/lib/lib-dynamodb/vitest.config.ts new file mode 100644 index 000000000000..f360902f18aa --- /dev/null +++ b/lib/lib-dynamodb/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e,browser}.spec.{ts,js}"], + include: ["**/*.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/lib/lib-storage/jest.config.e2e.js b/lib/lib-storage/jest.config.e2e.js deleted file mode 100644 index c3aa6055ef75..000000000000 --- a/lib/lib-storage/jest.config.e2e.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - preset: "ts-jest", - testMatch: ["**/*.e2e.spec.ts"], - bail: true, -}; diff --git a/lib/lib-storage/jest.config.js b/lib/lib-storage/jest.config.js deleted file mode 100644 index a8d1c2e49912..000000000000 --- a/lib/lib-storage/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, -}; diff --git a/lib/lib-storage/package.json b/lib/lib-storage/package.json index d9d81740c751..eb92859e7f09 100644 --- a/lib/lib-storage/package.json +++ b/lib/lib-storage/package.json @@ -14,8 +14,10 @@ "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", "extract:docs": "api-extractor run --local", - "test": "jest", - "test:e2e": "jest -c jest.config.e2e.js" + "test": "vitest run", + "test:e2e": "vitest run -c vitest.config.e2e.ts", + "test:watch": "vitest watch", + "test:e2e:watch": "vitest watch -c vitest.config.e2e.ts" }, "engines": { "node": ">=16.0.0" diff --git a/lib/lib-storage/src/Upload.spec.ts b/lib/lib-storage/src/Upload.spec.ts index e17572219236..ec946b810890 100644 --- a/lib/lib-storage/src/Upload.spec.ts +++ b/lib/lib-storage/src/Upload.spec.ts @@ -1,88 +1,100 @@ -const sendMock = jest.fn().mockImplementation((x) => x); -const createMultipartMock = jest.fn().mockResolvedValue({ - UploadId: "mockuploadId", -}); -const uploadPartMock = jest - .fn() - .mockResolvedValueOnce({ - ETag: "mock-upload-Etag", - }) - .mockResolvedValueOnce({ - ETag: "mock-upload-Etag-2", - }); -const putObjectMock = jest.fn().mockResolvedValue({ - ETag: "mockEtag", -}); -const completeMultipartMock = jest.fn().mockResolvedValue({ - Success: "This actually works!", -}); - -const putObjectTaggingMock = jest.fn().mockResolvedValue({ - Success: "Tags have been applied!", -}); +import { afterAll, afterEach, beforeEach, describe, expect, test as it, vi } from "vitest"; -let hostname = "s3.region.amazonaws.com"; -let port: number | undefined; -const endpointMock = jest.fn().mockImplementation(() => ({ - hostname, - port, - protocol: "https:", - path: "/", - query: undefined, -})); +/* eslint-disable no-var */ +var hostname = "s3.region.amazonaws.com"; +var port: number | undefined; import { EventEmitter, Readable } from "stream"; -const mockAddListener = jest.fn(); -const mockRemoveListener = jest.fn(); -const requestHandlerMock = (() => { - const mock = { - on: mockAddListener, - off: mockRemoveListener, +vi.mock("@aws-sdk/client-s3", async () => { + const sendMock = vi.fn().mockImplementation(async (x) => x); + const endpointMock = vi.fn().mockImplementation(() => ({ + hostname, + port, + protocol: "https:", + path: "/", + query: undefined, + })); + + const mockRequestHandler = new (class MockRequestHandler extends EventEmitter { + on = vi.fn(); + off = vi.fn(); + })(); + + return { + ...((await vi.importActual("@aws-sdk/client-s3")) as {}), + S3: vi.fn().mockReturnValue({ + send: sendMock, + config: { + endpoint: endpointMock, + }, + }), + S3Client: vi.fn().mockReturnValue({ + send: sendMock, + config: { + endpoint: endpointMock, + requestHandler: mockRequestHandler, + }, + }), + CreateMultipartUploadCommand: vi.fn().mockReturnValue({ + UploadId: "mockuploadId", + }), + UploadPartCommand: vi + .fn() + .mockReturnValueOnce({ + ETag: "mock-upload-Etag", + }) + .mockReturnValueOnce({ + ETag: "mock-upload-Etag-2", + }), + CompleteMultipartUploadCommand: vi.fn().mockReturnValue({ + Success: "This actually works!", + }), + PutObjectTaggingCommand: vi.fn().mockReturnValue({ + Success: "Tags have been applied!", + }), + PutObjectCommand: vi.fn().mockReturnValue({ + ETag: "mockEtag", + }), }; - Object.setPrototypeOf(mock, EventEmitter.prototype); - return mock; -})(); - -jest.mock("@aws-sdk/client-s3", () => ({ - ...(jest.requireActual("@aws-sdk/client-s3") as {}), - S3: jest.fn().mockReturnValue({ - send: sendMock, - config: { - endpoint: endpointMock, - }, - }), - S3Client: jest.fn().mockReturnValue({ - send: sendMock, - config: { - endpoint: endpointMock, - requestHandler: requestHandlerMock, - }, - }), - CreateMultipartUploadCommand: createMultipartMock, - UploadPartCommand: uploadPartMock, - CompleteMultipartUploadCommand: completeMultipartMock, - PutObjectTaggingCommand: putObjectTaggingMock, - PutObjectCommand: putObjectMock, -})); - -import { CompleteMultipartUploadCommandOutput, S3, S3Client } from "@aws-sdk/client-s3"; +}); + +import { + CompleteMultipartUploadCommand, + CompleteMultipartUploadCommandOutput, + CreateMultipartUploadCommand, + PutObjectCommand, + PutObjectTaggingCommand, + S3, + S3Client, + UploadPartCommand, +} from "@aws-sdk/client-s3"; import { AbortController } from "@smithy/abort-controller"; -import { createHash } from "crypto"; import { Progress, Upload } from "./index"; const DEFAULT_PART_SIZE = 1024 * 1024 * 5; describe(Upload.name, () => { + const s3MockInstance = new S3Client(); + beforeEach(() => { - jest.clearAllMocks(); - uploadPartMock + vi.clearAllMocks(); + vi.mocked(UploadPartCommand as any) + .mockReturnValueOnce({ + ETag: "mock-upload-Etag", + }) + .mockReturnValueOnce({ + ETag: "mock-upload-Etag-2", + }); + vi.mocked(CreateMultipartUploadCommand as any) .mockReset() - .mockResolvedValueOnce({ + .mockReturnValueOnce({ + UploadId: "mockuploadId", ETag: "mock-upload-Etag", }) - .mockResolvedValueOnce({ + .mockReturnValueOnce({ + UploadId: "mockuploadId", ETag: "mock-upload-Etag-2", }); }); @@ -93,24 +105,6 @@ describe(Upload.name, () => { Body: "this-is-a-sample-payload", }; - expect.extend({ - toHaveSameHashAsBuffer: (received: Uint8Array, expected: Uint8Array) => { - const receivedHash = createHash("sha256").update(received).digest("hex"); - const expectHash = createHash("sha256").update(expected).digest("hex"); - if (expectHash === receivedHash) { - return { - message: () => "received buffer has the correct hash", - pass: true, - }; - } else { - return { - message: () => `received buffer hash is incorrect, expect ${expectHash}, got ${receivedHash}.`, - pass: false, - }; - } - }, - }); - it("correctly exposes the event emitter API", () => { const upload = new Upload({ params, @@ -133,21 +127,21 @@ describe(Upload.name, () => { await upload.done(); - expect(sendMock).toHaveBeenCalledTimes(1); + expect(s3MockInstance.send).toHaveBeenCalledTimes(1); - expect(putObjectMock).toHaveBeenCalledTimes(1); - expect(putObjectMock).toHaveBeenCalledWith({ + expect(PutObjectCommand).toHaveBeenCalledTimes(1); + expect(PutObjectCommand).toHaveBeenCalledWith({ ...params, Body: Buffer.from(""), }); // create multipartMock is not called. - expect(createMultipartMock).toHaveBeenCalledTimes(0); + expect(CreateMultipartUploadCommand).toHaveBeenCalledTimes(0); // upload parts is not called. - expect(uploadPartMock).toHaveBeenCalledTimes(0); + expect(UploadPartCommand).toHaveBeenCalledTimes(0); // complete multipart upload is not called. - expect(completeMultipartMock).toHaveBeenCalledTimes(0); + expect(CompleteMultipartUploadCommand).toHaveBeenCalledTimes(0); // no tags were passed. - expect(putObjectTaggingMock).toHaveBeenCalledTimes(0); + expect(PutObjectTaggingCommand).toHaveBeenCalledTimes(0); }); it("should upload using PUT when empty stream", async () => { @@ -161,21 +155,21 @@ describe(Upload.name, () => { await upload.done(); - expect(sendMock).toHaveBeenCalledTimes(1); + expect(s3MockInstance.send).toHaveBeenCalledTimes(1); - expect(putObjectMock).toHaveBeenCalledTimes(1); - expect(putObjectMock).toHaveBeenCalledWith({ + expect(PutObjectCommand).toHaveBeenCalledTimes(1); + expect(PutObjectCommand).toHaveBeenCalledWith({ ...params, Body: Buffer.from(""), }); // create multipartMock is not called. - expect(createMultipartMock).toHaveBeenCalledTimes(0); + expect(CreateMultipartUploadCommand).toHaveBeenCalledTimes(0); // upload parts is not called. - expect(uploadPartMock).toHaveBeenCalledTimes(0); + expect(UploadPartCommand).toHaveBeenCalledTimes(0); // complete multipart upload is not called. - expect(completeMultipartMock).toHaveBeenCalledTimes(0); + expect(CompleteMultipartUploadCommand).toHaveBeenCalledTimes(0); // no tags were passed. - expect(putObjectTaggingMock).toHaveBeenCalledTimes(0); + expect(PutObjectTaggingCommand).toHaveBeenCalledTimes(0); }); it("should upload using PUT when parts are smaller than one part", async () => { @@ -186,21 +180,21 @@ describe(Upload.name, () => { await upload.done(); - expect(sendMock).toHaveBeenCalledTimes(1); + expect(s3MockInstance.send).toHaveBeenCalledTimes(1); - expect(putObjectMock).toHaveBeenCalledTimes(1); - expect(putObjectMock).toHaveBeenCalledWith({ + expect(PutObjectCommand).toHaveBeenCalledTimes(1); + expect(PutObjectCommand).toHaveBeenCalledWith({ ...params, Body: Buffer.from(params.Body), }); // create multipartMock is not called. - expect(createMultipartMock).toHaveBeenCalledTimes(0); + expect(CreateMultipartUploadCommand).toHaveBeenCalledTimes(0); // upload parts is not called. - expect(uploadPartMock).toHaveBeenCalledTimes(0); + expect(UploadPartCommand).toHaveBeenCalledTimes(0); // complete multipart upload is not called. - expect(completeMultipartMock).toHaveBeenCalledTimes(0); + expect(CompleteMultipartUploadCommand).toHaveBeenCalledTimes(0); // no tags were passed. - expect(putObjectTaggingMock).toHaveBeenCalledTimes(0); + expect(PutObjectTaggingCommand).toHaveBeenCalledTimes(0); }); it("should upload using PUT when parts are smaller than one part stream", async () => { @@ -216,21 +210,21 @@ describe(Upload.name, () => { await upload.done(); - expect(sendMock).toHaveBeenCalledTimes(1); + expect(s3MockInstance.send).toHaveBeenCalledTimes(1); - expect(putObjectMock).toHaveBeenCalledTimes(1); - expect(putObjectMock).toHaveBeenCalledWith({ + expect(PutObjectCommand).toHaveBeenCalledTimes(1); + expect(PutObjectCommand).toHaveBeenCalledWith({ ...params, Body: Buffer.from(params.Body), }); // create multipartMock is not called. - expect(createMultipartMock).toHaveBeenCalledTimes(0); + expect(CreateMultipartUploadCommand).toHaveBeenCalledTimes(0); // upload parts is not called. - expect(uploadPartMock).toHaveBeenCalledTimes(0); + expect(UploadPartCommand).toHaveBeenCalledTimes(0); // complete multipart upload is not called. - expect(completeMultipartMock).toHaveBeenCalledTimes(0); + expect(CompleteMultipartUploadCommand).toHaveBeenCalledTimes(0); // no tags were passed. - expect(putObjectTaggingMock).toHaveBeenCalledTimes(0); + expect(PutObjectTaggingCommand).toHaveBeenCalledTimes(0); }); it("should return a Bucket, Key and Location fields when upload uses a PUT", async () => { @@ -354,147 +348,157 @@ describe(Upload.name, () => { const partSize = 1024 * 1024 * 5; const largeBuffer = Buffer.from("#".repeat(partSize + 10)); const actionParams = { ...params, Body: largeBuffer }; - const completeMultipartMockWithLocation = completeMultipartMock.mockResolvedValueOnce({ - Location: "https://example-bucket.example-host.com/folder%2Fexample-key", - }); + const CompleteMultipartUploadCommandWithLocation = vi + .mocked(CompleteMultipartUploadCommand as any) + .mockResolvedValueOnce({ + Location: "https://example-bucket.example-host.com/folder%2Fexample-key", + }); const upload = new Upload({ params: actionParams, client: new S3({}), }); const result = (await upload.done()) as CompleteMultipartUploadCommandOutput; - expect(completeMultipartMockWithLocation).toHaveBeenCalledTimes(1); + expect(CompleteMultipartUploadCommandWithLocation).toHaveBeenCalledTimes(1); expect(result.Location).toEqual("https://example-bucket.example-host.com/folder/example-key"); }); - [ - { type: "buffer", largeBuffer: Buffer.from("#".repeat(DEFAULT_PART_SIZE + 10)) }, - { type: "Uint8array", largeBuffer: Uint8Array.from(Buffer.from("#".repeat(DEFAULT_PART_SIZE + 10))) }, - ].forEach(({ type, largeBuffer }) => { - it(`should upload using multi-part when parts are larger than part size ${type}`, async () => { - const firstBuffer = largeBuffer.subarray(0, DEFAULT_PART_SIZE); - const secondBuffer = largeBuffer.subarray(DEFAULT_PART_SIZE); - const actionParams = { ...params, Body: largeBuffer }; - const upload = new Upload({ - params: actionParams, - client: new S3({}), - }); - await upload.done(); - expect(sendMock).toHaveBeenCalledTimes(4); - // create multipartMock is called correctly. - expect(createMultipartMock).toHaveBeenCalledTimes(1); - expect(createMultipartMock).toHaveBeenCalledWith({ - ...actionParams, - Body: undefined, - }); - // upload parts is called correctly. - expect(uploadPartMock).toHaveBeenCalledTimes(2); - expect(uploadPartMock).toHaveBeenNthCalledWith(1, { - ...actionParams, - // @ts-ignore extended custom matcher - Body: expect.toHaveSameHashAsBuffer(firstBuffer), - PartNumber: 1, - UploadId: "mockuploadId", - }); - expect(uploadPartMock).toHaveBeenNthCalledWith(2, { - ...actionParams, - // @ts-ignore extended custom matcher - Body: expect.toHaveSameHashAsBuffer(secondBuffer), - PartNumber: 2, - UploadId: "mockuploadId", - }); - // complete multipart upload is called correctly. - expect(completeMultipartMock).toHaveBeenCalledTimes(1); - expect(completeMultipartMock).toHaveBeenLastCalledWith({ - ...actionParams, - Body: undefined, - UploadId: "mockuploadId", - MultipartUpload: { - Parts: [ - { - ETag: "mock-upload-Etag", - PartNumber: 1, - }, - { - ETag: "mock-upload-Etag-2", - PartNumber: 2, - }, - ], - }, + describe("large buffers", () => { + const MOCK_PART_SIZE = 24; + beforeEach(() => { + (Upload as any).MIN_PART_SIZE = MOCK_PART_SIZE; + }); + + afterAll(() => { + (Upload as any).MIN_PART_SIZE = 1024 * 1024 * 5; + }); + + [ + { type: "buffer", largeBuffer: Buffer.from("#".repeat(MOCK_PART_SIZE + 10)) }, + { type: "Uint8array", largeBuffer: Uint8Array.from(Buffer.from("#".repeat(MOCK_PART_SIZE + 10))) }, + ].forEach(({ type, largeBuffer }) => { + it(`should upload using multi-part when parts are larger than part size ${type}`, async () => { + const firstBuffer = largeBuffer.subarray(0, MOCK_PART_SIZE); + const secondBuffer = largeBuffer.subarray(MOCK_PART_SIZE); + const actionParams = { ...params, Body: largeBuffer }; + const upload = new Upload({ + params: actionParams, + partSize: MOCK_PART_SIZE, + client: new S3({}), + }); + await upload.done(); + expect(s3MockInstance.send).toHaveBeenCalledTimes(4); + // create multipartMock is called correctly. + expect(CreateMultipartUploadCommand).toHaveBeenCalledTimes(1); + expect(CreateMultipartUploadCommand).toHaveBeenCalledWith({ + ...actionParams, + Body: undefined, + }); + // upload parts is called correctly. + expect(UploadPartCommand).toHaveBeenCalledTimes(2); + expect(UploadPartCommand).toHaveBeenNthCalledWith(1, { + ...actionParams, + Body: firstBuffer, + PartNumber: 1, + UploadId: "mockuploadId", + }); + expect(UploadPartCommand).toHaveBeenNthCalledWith(2, { + ...actionParams, + Body: secondBuffer, + PartNumber: 2, + UploadId: "mockuploadId", + }); + // complete multipart upload is called correctly. + expect(CompleteMultipartUploadCommand).toHaveBeenCalledTimes(1); + expect(CompleteMultipartUploadCommand).toHaveBeenLastCalledWith({ + ...actionParams, + Body: undefined, + UploadId: "mockuploadId", + MultipartUpload: { + Parts: [ + { + ETag: "mock-upload-Etag", + PartNumber: 1, + }, + { + ETag: "mock-upload-Etag-2", + PartNumber: 2, + }, + ], + }, + }); + + // no tags were passed. + expect(PutObjectTaggingCommand).toHaveBeenCalledTimes(0); + // put was not called + expect(PutObjectCommand).toHaveBeenCalledTimes(0); }); - // no tags were passed. - expect(putObjectTaggingMock).toHaveBeenCalledTimes(0); - // put was not called - expect(putObjectMock).toHaveBeenCalledTimes(0); - }); - - it("should upload using multi-part when parts are larger than part size stream", async () => { - // create a string that's larger than 5MB. - const firstBuffer = largeBuffer.subarray(0, DEFAULT_PART_SIZE); - const secondBuffer = largeBuffer.subarray(DEFAULT_PART_SIZE); - const streamBody = Readable.from( - (function* () { - yield largeBuffer; - })() - ); - const actionParams = { ...params, Body: streamBody }; - const upload = new Upload({ - params: actionParams, - client: new S3({}), + it("should upload using multi-part when parts are larger than part size stream", async () => { + // create a string that's larger than 5MB. + const firstBuffer = largeBuffer.subarray(0, MOCK_PART_SIZE); + const secondBuffer = largeBuffer.subarray(MOCK_PART_SIZE); + const streamBody = Readable.from( + (function* () { + yield largeBuffer; + })() + ); + const actionParams = { ...params, Body: streamBody }; + const upload = new Upload({ + params: actionParams, + partSize: MOCK_PART_SIZE, + client: new S3({}), + }); + + await upload.done(); + + expect(s3MockInstance.send).toHaveBeenCalledTimes(4); + // create multipartMock is called correctly. + expect(CreateMultipartUploadCommand).toHaveBeenCalledTimes(1); + expect(CreateMultipartUploadCommand).toHaveBeenCalledWith({ + ...actionParams, + Body: undefined, + }); + + // upload parts is called correctly. + expect(UploadPartCommand).toHaveBeenCalledTimes(2); + + expect(UploadPartCommand).toHaveBeenNthCalledWith(1, { + ...actionParams, + Body: firstBuffer, + PartNumber: 1, + UploadId: "mockuploadId", + }); + expect(UploadPartCommand).toHaveBeenNthCalledWith(2, { + ...actionParams, + // @ts-ignore + Body: secondBuffer, + PartNumber: 2, + UploadId: "mockuploadId", + }); + // complete multipart upload is called correctly. + expect(CompleteMultipartUploadCommand).toHaveBeenCalledTimes(1); + expect(CompleteMultipartUploadCommand).toHaveBeenLastCalledWith({ + ...actionParams, + Body: undefined, + UploadId: "mockuploadId", + MultipartUpload: { + Parts: [ + { + ETag: "mock-upload-Etag", + PartNumber: 1, + }, + { + ETag: "mock-upload-Etag-2", + PartNumber: 2, + }, + ], + }, + }); + // no tags were passed. + expect(PutObjectTaggingCommand).toHaveBeenCalledTimes(0); + // put was not called + expect(PutObjectCommand).toHaveBeenCalledTimes(0); }); - - await upload.done(); - - expect(sendMock).toHaveBeenCalledTimes(4); - // create multipartMock is called correctly. - expect(createMultipartMock).toHaveBeenCalledTimes(1); - expect(createMultipartMock).toHaveBeenCalledWith({ - ...actionParams, - Body: undefined, - }); - - // upload parts is called correctly. - expect(uploadPartMock).toHaveBeenCalledTimes(2); - expect(uploadPartMock).toHaveBeenNthCalledWith(1, { - ...actionParams, - // @ts-ignore extended custom matcher - Body: expect.toHaveSameHashAsBuffer(firstBuffer), - PartNumber: 1, - UploadId: "mockuploadId", - }); - - expect(uploadPartMock).toHaveBeenNthCalledWith(2, { - ...actionParams, - // @ts-ignore extended custom matcher - Body: expect.toHaveSameHashAsBuffer(secondBuffer), - PartNumber: 2, - UploadId: "mockuploadId", - }); - - // complete multipart upload is called correctly. - expect(completeMultipartMock).toHaveBeenCalledTimes(1); - expect(completeMultipartMock).toHaveBeenLastCalledWith({ - ...actionParams, - Body: undefined, - UploadId: "mockuploadId", - MultipartUpload: { - Parts: [ - { - ETag: "mock-upload-Etag", - PartNumber: 1, - }, - { - ETag: "mock-upload-Etag-2", - PartNumber: 2, - }, - ], - }, - }); - - // no tags were passed. - expect(putObjectTaggingMock).toHaveBeenCalledTimes(0); - // put was not called - expect(putObjectMock).toHaveBeenCalledTimes(0); }); }); @@ -518,11 +522,11 @@ describe(Upload.name, () => { await upload.done(); - expect(sendMock).toHaveBeenCalledTimes(2); + expect(s3MockInstance.send).toHaveBeenCalledTimes(2); // tags were passed. - expect(putObjectTaggingMock).toHaveBeenCalledTimes(1); - expect(putObjectTaggingMock).toHaveBeenCalledWith({ + expect(PutObjectTaggingCommand).toHaveBeenCalledTimes(1); + expect(PutObjectTaggingCommand).toHaveBeenCalledWith({ ...params, Tagging: { TagSet: tags, @@ -552,14 +556,13 @@ describe(Upload.name, () => { await upload.done(); - expect(sendMock).toHaveBeenCalledTimes(5); + expect(s3MockInstance.send).toHaveBeenCalledTimes(5); // tags were passed. - expect(putObjectTaggingMock).toHaveBeenCalledTimes(1); - expect(putObjectTaggingMock).toHaveBeenCalledWith({ + expect(PutObjectTaggingCommand).toHaveBeenCalledTimes(1); + expect(PutObjectTaggingCommand).toHaveBeenCalledWith({ ...actionParams, - // @ts-ignore extended custom matcher - Body: expect.toHaveSameHashAsBuffer(largeBuffer), + Body: largeBuffer, Tagging: { TagSet: tags, }, @@ -625,21 +628,22 @@ describe(Upload.name, () => { received.push(progress); }); await upload.done(); - expect(received[0]).toEqual({ - Key: params.Key, - Bucket: params.Bucket, - loaded: firstBuffer.byteLength, - part: 1, - total: largeBuffer.byteLength, - }); - expect(received[1]).toEqual({ - Key: params.Key, - Bucket: params.Bucket, - loaded: largeBuffer.byteLength, - part: 2, - total: largeBuffer.byteLength, - }); - expect(received.length).toBe(2); + expect(received).toEqual([ + { + Key: params.Key, + Bucket: params.Bucket, + loaded: firstBuffer.byteLength, + part: 1, + total: largeBuffer.byteLength, + }, + { + Key: params.Key, + Bucket: params.Bucket, + loaded: largeBuffer.byteLength, + part: 2, + total: largeBuffer.byteLength, + }, + ]); }); it("should provide progress updates multi-part stream", async () => { @@ -661,21 +665,22 @@ describe(Upload.name, () => { received.push(progress); }); await upload.done(); - expect(received[0]).toEqual({ - Key: params.Key, - Bucket: params.Bucket, - loaded: partSize, - part: 1, - total: undefined, - }); - expect(received[1]).toEqual({ - Key: params.Key, - Bucket: params.Bucket, - loaded: partSize + 10, - part: 2, - total: undefined, - }); - expect(received.length).toBe(2); + expect(received).toEqual([ + { + Key: params.Key, + Bucket: params.Bucket, + loaded: partSize, + part: 1, + total: undefined, + }, + { + Key: params.Key, + Bucket: params.Bucket, + loaded: partSize + 10, + part: 2, + total: undefined, + }, + ]); }); it("should provide progress updates empty buffer", async () => { @@ -744,8 +749,14 @@ describe(Upload.name, () => { }); await upload.done(); expect(received.length).toBe(2); - expect(mockAddListener).toHaveBeenCalledWith("xhr.upload.progress", expect.any(Function)); - expect(mockRemoveListener).toHaveBeenCalledWith("xhr.upload.progress", expect.any(Function)); + expect((s3MockInstance.config.requestHandler as unknown as EventEmitter).on).toHaveBeenCalledWith( + "xhr.upload.progress", + expect.any(Function) + ); + expect((s3MockInstance.config.requestHandler as unknown as EventEmitter).off).toHaveBeenCalledWith( + "xhr.upload.progress", + expect.any(Function) + ); }); it("should respect external abort signal", async () => { diff --git a/lib/lib-storage/src/Upload.ts b/lib/lib-storage/src/Upload.ts index c702d36dc4f0..a548696db9bc 100644 --- a/lib/lib-storage/src/Upload.ts +++ b/lib/lib-storage/src/Upload.ts @@ -33,9 +33,12 @@ export interface RawDataPart { lastPart?: boolean; } -const MIN_PART_SIZE = 1024 * 1024 * 5; - export class Upload extends EventEmitter { + /** + * @internal + * modified in testing only. + */ + private static MIN_PART_SIZE = 1024 * 1024 * 5; /** * S3 multipart upload does not allow more than 10,000 parts. */ @@ -43,7 +46,7 @@ export class Upload extends EventEmitter { // Defaults. private readonly queueSize: number = 4; - private readonly partSize = MIN_PART_SIZE; + private readonly partSize = Upload.MIN_PART_SIZE; private readonly leavePartsOnError: boolean = false; private readonly tags: Tag[] = []; @@ -428,9 +431,9 @@ export class Upload extends EventEmitter { throw new Error(`InputError: Upload requires a AWS client to do uploads with.`); } - if (this.partSize < MIN_PART_SIZE) { + if (this.partSize < Upload.MIN_PART_SIZE) { throw new Error( - `EntityTooSmall: Your proposed upload partsize [${this.partSize}] is smaller than the minimum allowed size [${MIN_PART_SIZE}] (5MB)` + `EntityTooSmall: Your proposed upload partsize [${this.partSize}] is smaller than the minimum allowed size [${Upload.MIN_PART_SIZE}] (5MB)` ); } diff --git a/lib/lib-storage/src/chunks/getChunkUint8Array.spec.ts b/lib/lib-storage/src/chunks/getChunkUint8Array.spec.ts index eae36ca8c07c..664ee546de7c 100644 --- a/lib/lib-storage/src/chunks/getChunkUint8Array.spec.ts +++ b/lib/lib-storage/src/chunks/getChunkUint8Array.spec.ts @@ -1,3 +1,5 @@ +import { describe, expect, test as it } from "vitest"; + import { byteLength } from "../bytelength"; import { RawDataPart } from "../Upload"; import { getChunkUint8Array } from "./getChunkUint8Array"; diff --git a/lib/lib-storage/src/chunks/getDataReadable.spec.ts b/lib/lib-storage/src/chunks/getDataReadable.spec.ts index d33e974b2dff..6c57baafc066 100644 --- a/lib/lib-storage/src/chunks/getDataReadable.spec.ts +++ b/lib/lib-storage/src/chunks/getDataReadable.spec.ts @@ -1,4 +1,5 @@ import { Readable } from "stream"; +import { describe, expect, test as it } from "vitest"; import { byteLength } from "../bytelength"; import { RawDataPart as DataPart } from "../Upload"; diff --git a/lib/lib-storage/src/chunks/getDataReadableStream.spec.ts b/lib/lib-storage/src/chunks/getDataReadableStream.spec.ts index bd382f0b7b98..6cd11135bbb8 100644 --- a/lib/lib-storage/src/chunks/getDataReadableStream.spec.ts +++ b/lib/lib-storage/src/chunks/getDataReadableStream.spec.ts @@ -1,3 +1,4 @@ +import { describe, expect, test as it } from "vitest"; // polyfill exposes the same ReadableStream API as web, allowing easy testing import { ReadableStream } from "web-streams-polyfill"; diff --git a/lib/lib-storage/src/index.spec.ts b/lib/lib-storage/src/index.spec.ts index 9b3931a6beaf..e37707c04a53 100644 --- a/lib/lib-storage/src/index.spec.ts +++ b/lib/lib-storage/src/index.spec.ts @@ -1,3 +1,5 @@ +import { describe, expect, test as it } from "vitest"; + import * as Storage from "./index"; describe("Storage Packages", () => { diff --git a/lib/lib-storage/src/lib-storage.e2e.spec.ts b/lib/lib-storage/src/lib-storage.e2e.spec.ts index 61842fd57cbf..1344a5b61ddd 100644 --- a/lib/lib-storage/src/lib-storage.e2e.spec.ts +++ b/lib/lib-storage/src/lib-storage.e2e.spec.ts @@ -3,21 +3,35 @@ import { Upload } from "@aws-sdk/lib-storage"; import type { AwsCredentialIdentity } from "@smithy/types"; import { randomBytes } from "crypto"; import { Readable } from "stream"; +import { afterAll, beforeAll, describe, expect, test as it } from "vitest"; -const region: string | undefined = process?.env?.AWS_SMOKE_TEST_REGION; -const credentials: AwsCredentialIdentity | undefined = (globalThis as any).credentials || undefined; -const Bucket = process?.env?.AWS_SMOKE_TEST_BUCKET; - -jest.setTimeout(45_000); +import { getIntegTestResources } from "../../../tests/e2e/get-integ-test-resources"; describe("@aws-sdk/lib-storage", () => { - let Key = ``; - const data = randomBytes(20_240_000); - const dataString = data.toString(); + let Key: string; + let client: S3; + let data: Uint8Array; + let dataString: string; + let Bucket: string; + let region: string; + let credentials: AwsCredentialIdentity; + + beforeAll(async () => { + const integTestResourcesEnv = await getIntegTestResources(); + Object.assign(process.env, integTestResourcesEnv); + + region = process?.env?.AWS_SMOKE_TEST_REGION as string; + credentials = (globalThis as any).credentials || undefined; + Bucket = process?.env?.AWS_SMOKE_TEST_BUCKET as string; - const client = new S3({ - region, - credentials, + Key = ``; + data = randomBytes(20_240_000); + dataString = data.toString(); + + client = new S3({ + region, + credentials, + }); }); describe("Upload", () => { @@ -28,26 +42,62 @@ describe("@aws-sdk/lib-storage", () => { await client.deleteObject({ Bucket, Key }); }); - for (const body of [data, dataString, Readable.from(data)]) { - it("should upload in parts for input type " + body.constructor.name, async () => { - const s3Upload = new Upload({ - client, - params: { - Bucket, - Key, - Body: body, - }, - }); - await s3Upload.done(); - - const object = await client.getObject({ + it("should upload in parts for input type bytes", async () => { + const s3Upload = new Upload({ + client, + params: { Bucket, Key, - }); + Body: data, + }, + }); + await s3Upload.done(); - expect(await object.Body?.transformToString()).toEqual(dataString); + const object = await client.getObject({ + Bucket, + Key, }); - } + + expect(await object.Body?.transformToString()).toEqual(dataString); + }); + + it("should upload in parts for input type string", async () => { + const s3Upload = new Upload({ + client, + params: { + Bucket, + Key, + Body: dataString, + }, + }); + await s3Upload.done(); + + const object = await client.getObject({ + Bucket, + Key, + }); + + expect(await object.Body?.transformToString()).toEqual(dataString); + }); + + it("should upload in parts for input type Readable", async () => { + const s3Upload = new Upload({ + client, + params: { + Bucket, + Key, + Body: Readable.from(data), + }, + }); + await s3Upload.done(); + + const object = await client.getObject({ + Bucket, + Key, + }); + + expect(await object.Body?.transformToString()).toEqual(dataString); + }); it("should call AbortMultipartUpload if unable to complete a multipart upload.", async () => { class MockFailureS3 extends S3 { @@ -109,4 +159,4 @@ describe("@aws-sdk/lib-storage", () => { ]); }); }); -}); +}, 45_000); diff --git a/lib/lib-storage/vitest.config.e2e.ts b/lib/lib-storage/vitest.config.e2e.ts new file mode 100644 index 000000000000..8e3e8ccc5726 --- /dev/null +++ b/lib/lib-storage/vitest.config.e2e.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["**/*.e2e.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/lib/lib-storage/vitest.config.ts b/lib/lib-storage/vitest.config.ts new file mode 100644 index 000000000000..f360902f18aa --- /dev/null +++ b/lib/lib-storage/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e,browser}.spec.{ts,js}"], + include: ["**/*.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/package.json b/package.json index 233d57b145fd..1261ba08c124 100644 --- a/package.json +++ b/package.json @@ -39,13 +39,13 @@ "test:e2e:legacy:preview": "./tests/e2e-legacy/preview.mjs", "test:e2e:legacy:since:release": "./tests/e2e-legacy/since-release.mjs", "test:functional": "jest --passWithNoTests --config tests/functional/jest.config.js && lerna run test:unit --scope \"@aws-sdk/client-*\"", - "test:integration": "jest --config jest.config.integ.js --passWithNoTests", + "test:integration": "node ./scripts/turbo test:integration", "test:integration:legacy": "yarn test:e2e:legacy", "test:integration:legacy:since:release": "yarn test:e2e:legacy:since:release", "test:protocols": "yarn build:protocols && lerna run test --scope '@aws-sdk/aws-protocoltests-*'", "test:server-protocols": "yarn build:server-protocols && lerna run test --scope '@aws-sdk/*-server'", "test:size": "cd scripts/benchmark-size/runner && yarn && ts-node ./cli.ts", - "test:unit": "jest --config jest.config.js", + "test:unit": "node ./scripts/turbo test", "test:versions": "jest --config tests/versions/jest.config.js tests/versions/index.spec.ts", "update:versions:default": "node --es-module-specifier-resolution=node ./scripts/update-versions/default.mjs", "update:versions:current": "node --es-module-specifier-resolution=node ./scripts/update-versions/current.mjs" @@ -92,6 +92,7 @@ "fs-extra": "^9.0.0", "generate-changelog": "^1.7.1", "glob": "7.1.6", + "happy-dom": "14.12.3", "husky": "^4.2.3", "jasmine-core": "^3.5.0", "jest": "29.7.0", diff --git a/packages/body-checksum-browser/jest.config.js b/packages/body-checksum-browser/jest.config.js deleted file mode 100644 index bd895a5df03e..000000000000 --- a/packages/body-checksum-browser/jest.config.js +++ /dev/null @@ -1,6 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, - testEnvironment: "jsdom", -}; diff --git a/packages/body-checksum-browser/package.json b/packages/body-checksum-browser/package.json index ff1ddf53e2e1..58e88ce24883 100644 --- a/packages/body-checksum-browser/package.json +++ b/packages/body-checksum-browser/package.json @@ -9,7 +9,8 @@ "build:types": "tsc -p tsconfig.types.json", "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", - "test": "jest" + "test": "vitest run", + "test:watch": "vitest watch" }, "main": "./dist-cjs/index.js", "module": "./dist-es/index.js", @@ -34,6 +35,7 @@ "@tsconfig/recommended": "1.0.1", "concurrently": "7.0.0", "downlevel-dts": "0.10.1", + "happy-dom": "^15.7.4", "rimraf": "3.0.2", "typescript": "~4.9.5" }, diff --git a/packages/body-checksum-browser/src/index.spec.ts b/packages/body-checksum-browser/src/index.spec.ts index ddbc91434305..c129098ce69c 100644 --- a/packages/body-checksum-browser/src/index.spec.ts +++ b/packages/body-checksum-browser/src/index.spec.ts @@ -2,6 +2,7 @@ import { Sha256 } from "@aws-crypto/sha256-js"; import { HttpRequest } from "@smithy/protocol-http"; import { fromUtf8 } from "@smithy/util-utf8"; import { Readable } from "stream"; +import { describe, expect, test as it } from "vitest"; import { bodyChecksumGenerator } from "."; @@ -54,7 +55,9 @@ describe("bodyChecksumGenerator for browser", () => { try { await bodyChecksumGenerator(request, options); } catch (e) { - expect(e).toEqual(new Error("Unable to calculate checksums for non-blob streams.")); + expect(e).toEqual( + new Error("Unable to calculate checksums for non-blob streams, received: Readable:[object Object].") + ); } }); }); diff --git a/packages/body-checksum-browser/src/index.ts b/packages/body-checksum-browser/src/index.ts index fd60de7a628b..844554780aed 100644 --- a/packages/body-checksum-browser/src/index.ts +++ b/packages/body-checksum-browser/src/index.ts @@ -23,7 +23,10 @@ export async function bodyChecksumGenerator( contentHash.update(toUint8Array(body)); treeHash.update(body); } else { - if (Boolean(body) && Object.prototype.toString.call(body) === "[object Blob]") { + if ( + Boolean(body) && + (Object.prototype.toString.call(body) === "[object Blob]" || body.constructor?.name === "Blob") + ) { await blobReader( body, (chunk: any) => { @@ -33,7 +36,11 @@ export async function bodyChecksumGenerator( MiB ); } else { - throw new Error("Unable to calculate checksums for non-blob streams."); + throw new Error( + `Unable to calculate checksums for non-blob streams, received: ${ + body.constructor.name + ":" + Object.prototype.toString.call(body) + }.` + ); } } diff --git a/packages/body-checksum-browser/vitest.config.ts b/packages/body-checksum-browser/vitest.config.ts new file mode 100644 index 000000000000..93500dad24d6 --- /dev/null +++ b/packages/body-checksum-browser/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e}.spec.{ts,js}"], + include: ["**/*.spec.{ts,js}"], + environment: "happy-dom", + }, +}); diff --git a/packages/body-checksum-node/jest.config.js b/packages/body-checksum-node/jest.config.js deleted file mode 100644 index a8d1c2e49912..000000000000 --- a/packages/body-checksum-node/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, -}; diff --git a/packages/body-checksum-node/package.json b/packages/body-checksum-node/package.json index 88a1a9045214..3bf6601b7ca8 100644 --- a/packages/body-checksum-node/package.json +++ b/packages/body-checksum-node/package.json @@ -9,7 +9,8 @@ "build:types": "tsc -p tsconfig.types.json", "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", - "test": "jest" + "test": "vitest run", + "test:watch": "vitest watch" }, "main": "./dist-cjs/index.js", "module": "./dist-es/index.js", diff --git a/packages/body-checksum-node/src/index.spec.ts b/packages/body-checksum-node/src/index.spec.ts index 441be571838e..024b70fa881b 100644 --- a/packages/body-checksum-node/src/index.spec.ts +++ b/packages/body-checksum-node/src/index.spec.ts @@ -5,6 +5,7 @@ import { createReadStream, mkdtempSync, writeFileSync } from "fs"; import { tmpdir } from "os"; import { join } from "path"; import { Readable } from "stream"; +import { describe, expect, test as it } from "vitest"; import { bodyChecksumGenerator } from "."; diff --git a/packages/body-checksum-node/vitest.config.ts b/packages/body-checksum-node/vitest.config.ts new file mode 100644 index 000000000000..f360902f18aa --- /dev/null +++ b/packages/body-checksum-node/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e,browser}.spec.{ts,js}"], + include: ["**/*.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/packages/chunked-stream-reader-node/jest.config.js b/packages/chunked-stream-reader-node/jest.config.js deleted file mode 100644 index a8d1c2e49912..000000000000 --- a/packages/chunked-stream-reader-node/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, -}; diff --git a/packages/chunked-stream-reader-node/package.json b/packages/chunked-stream-reader-node/package.json index b7d9344cf3a0..3c03f8815e0f 100644 --- a/packages/chunked-stream-reader-node/package.json +++ b/packages/chunked-stream-reader-node/package.json @@ -9,7 +9,8 @@ "build:types": "tsc -p tsconfig.types.json", "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", - "test": "jest" + "test": "vitest run", + "test:watch": "vitest watch" }, "main": "./dist-cjs/index.js", "module": "./dist-es/index.js", diff --git a/packages/chunked-stream-reader-node/src/index.spec.ts b/packages/chunked-stream-reader-node/src/index.spec.ts index db56d2b900c9..be51b7584bc3 100644 --- a/packages/chunked-stream-reader-node/src/index.spec.ts +++ b/packages/chunked-stream-reader-node/src/index.spec.ts @@ -1,4 +1,5 @@ import { PassThrough } from "stream"; +import { describe, expect, test as it, vi } from "vitest"; import { streamReader } from "./index"; import { ReadFromBuffers } from "./readable.fixture"; @@ -8,7 +9,7 @@ describe("streamReader", () => { const buffers = [Buffer.alloc(1024, 0), Buffer.alloc(1024, 1), Buffer.alloc(1024, 2), Buffer.alloc(1024, 3)]; const mockStream = new ReadFromBuffers({ buffers }); - const mockChunkReader = jest.fn(); + const mockChunkReader = vi.fn(); await streamReader(mockStream, mockChunkReader, 1024); const mockChunkCalls = mockChunkReader.mock.calls; @@ -28,7 +29,7 @@ describe("streamReader", () => { ]; const mockStream = new ReadFromBuffers({ buffers }); - const mockChunkReader = jest.fn(); + const mockChunkReader = vi.fn(); await streamReader(mockStream, mockChunkReader); const mockChunkCalls = mockChunkReader.mock.calls; @@ -43,7 +44,7 @@ describe("streamReader", () => { const buffers = [Buffer.alloc(1000, 0), Buffer.alloc(1000, 1), Buffer.alloc(1000, 2), Buffer.alloc(600, 3)]; const mockStream = new ReadFromBuffers({ buffers }); - const mockChunkReader = jest.fn(); + const mockChunkReader = vi.fn(); await streamReader(mockStream, mockChunkReader, 500); @@ -69,7 +70,7 @@ describe("streamReader", () => { ]; const mockStream = new ReadFromBuffers({ buffers }); - const mockChunkReader = jest.fn(); + const mockChunkReader = vi.fn(); await streamReader(mockStream, mockChunkReader, 500); @@ -83,7 +84,7 @@ describe("streamReader", () => { const buffers = [Buffer.alloc(100, 0), Buffer.alloc(100, 1), Buffer.alloc(100, 2)]; const mockStream = new ReadFromBuffers({ buffers }); - const mockChunkReader = jest.fn(); + const mockChunkReader = vi.fn(); await streamReader(mockStream, mockChunkReader, 500); @@ -98,7 +99,7 @@ describe("streamReader", () => { const payload = Buffer.alloc(700, 0); mockStream.end(payload); - const mockChunkReader = jest.fn(); + const mockChunkReader = vi.fn(); await streamReader(mockStream, mockChunkReader, 500); const mockChunkCalls = mockChunkReader.mock.calls; @@ -123,7 +124,7 @@ describe("streamReader", () => { errorAfter: 2, // throw error after 2 chunks have been read }); - const mockChunkReader = jest.fn(); + const mockChunkReader = vi.fn(); await expect(streamReader(mockStream, mockChunkReader, 500)).rejects.toHaveProperty("message"); }); diff --git a/packages/chunked-stream-reader-node/vitest.config.ts b/packages/chunked-stream-reader-node/vitest.config.ts new file mode 100644 index 000000000000..f360902f18aa --- /dev/null +++ b/packages/chunked-stream-reader-node/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e,browser}.spec.{ts,js}"], + include: ["**/*.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/packages/cloudfront-signer/jest.config.js b/packages/cloudfront-signer/jest.config.js deleted file mode 100644 index f2c48a84f355..000000000000 --- a/packages/cloudfront-signer/jest.config.js +++ /dev/null @@ -1,6 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, - testPathIgnorePatterns: ["/node_modules/"], -}; diff --git a/packages/cloudfront-signer/package.json b/packages/cloudfront-signer/package.json index 26fe8f2a2845..130e0f7e563b 100644 --- a/packages/cloudfront-signer/package.json +++ b/packages/cloudfront-signer/package.json @@ -10,7 +10,8 @@ "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", "extract:docs": "api-extractor run --local", - "test": "jest" + "test": "vitest run", + "test:watch": "vitest watch" }, "main": "./dist-cjs/index.js", "module": "./dist-es/index.js", diff --git a/packages/cloudfront-signer/src/sign.spec.ts b/packages/cloudfront-signer/src/sign.spec.ts index c1f2e8735b40..4903525cd843 100644 --- a/packages/cloudfront-signer/src/sign.spec.ts +++ b/packages/cloudfront-signer/src/sign.spec.ts @@ -1,5 +1,6 @@ import { parseUrl } from "@smithy/url-parser"; import { createSign, createVerify } from "crypto"; +import { describe, expect, test as it } from "vitest"; import { getSignedCookies, getSignedUrl } from "./index"; diff --git a/packages/cloudfront-signer/vitest.config.ts b/packages/cloudfront-signer/vitest.config.ts new file mode 100644 index 000000000000..f360902f18aa --- /dev/null +++ b/packages/cloudfront-signer/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e,browser}.spec.{ts,js}"], + include: ["**/*.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/packages/core/integ/request-handlers/request-handlers.integ.spec.ts b/packages/core/integ/request-handlers/request-handlers.integ.spec.ts index d4f603c506e5..c1bf527547a5 100644 --- a/packages/core/integ/request-handlers/request-handlers.integ.spec.ts +++ b/packages/core/integ/request-handlers/request-handlers.integ.spec.ts @@ -1,5 +1,3 @@ -import { test as it, describe, expect } from "vitest"; - import { Kinesis } from "@aws-sdk/client-kinesis"; import { S3 } from "@aws-sdk/client-s3"; import { TranscribeStreaming } from "@aws-sdk/client-transcribe-streaming"; @@ -8,6 +6,7 @@ import { XhrHttpHandler } from "@aws-sdk/xhr-http-handler"; import { FetchHttpHandler } from "@smithy/fetch-http-handler"; import { NodeHttp2Handler, NodeHttpHandler } from "@smithy/node-http-handler"; import { Agent } from "https"; +import { describe, expect, test as it } from "vitest"; describe("request handler initialization", () => { describe("http", () => { diff --git a/packages/core/package.json b/packages/core/package.json index 89972935bbe5..ca2436cf3758 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -13,7 +13,9 @@ "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", "extract:docs": "api-extractor run --local", "test": "vitest run", - "test:integration": "vitest run -c vitest.config.integ.ts" + "test:integration": "vitest run -c vitest.config.integ.ts", + "test:watch": "vitest watch", + "test:integration:watch": "vitest watch -c vitest.config.integ.ts" }, "main": "./dist-cjs/index.js", "module": "./dist-es/index.js", diff --git a/packages/credential-provider-cognito-identity/jest.config.js b/packages/credential-provider-cognito-identity/jest.config.js deleted file mode 100644 index 64f3d932819c..000000000000 --- a/packages/credential-provider-cognito-identity/jest.config.js +++ /dev/null @@ -1,7 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, - //only test cjs dist, avoid testing the package twice - testPathIgnorePatterns: ["/node_modules/", "/es/"], -}; diff --git a/packages/credential-provider-cognito-identity/package.json b/packages/credential-provider-cognito-identity/package.json index 79498cf8792c..49a71fb3348a 100644 --- a/packages/credential-provider-cognito-identity/package.json +++ b/packages/credential-provider-cognito-identity/package.json @@ -9,7 +9,8 @@ "build:types": "tsc -p tsconfig.types.json", "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", - "test": "jest" + "test": "vitest run", + "test:watch": "vitest watch" }, "main": "./dist-cjs/index.js", "module": "./dist-es/index.js", diff --git a/packages/credential-provider-cognito-identity/src/InMemoryStorage.spec.ts b/packages/credential-provider-cognito-identity/src/InMemoryStorage.spec.ts index 5c22130887f8..fabe323ed0ad 100644 --- a/packages/credential-provider-cognito-identity/src/InMemoryStorage.spec.ts +++ b/packages/credential-provider-cognito-identity/src/InMemoryStorage.spec.ts @@ -1,3 +1,5 @@ +import { describe, expect, test as it } from "vitest"; + import { InMemoryStorage } from "./InMemoryStorage"; describe("InMemoryStorage", () => { diff --git a/packages/credential-provider-cognito-identity/src/IndexedDbStorage.spec.ts b/packages/credential-provider-cognito-identity/src/IndexedDbStorage.spec.ts index ab7ae312d1de..85c752575e42 100644 --- a/packages/credential-provider-cognito-identity/src/IndexedDbStorage.spec.ts +++ b/packages/credential-provider-cognito-identity/src/IndexedDbStorage.spec.ts @@ -1,7 +1,9 @@ +import { describe, expect, test as it } from "vitest"; + import { IndexedDbStorage } from "./IndexedDbStorage"; describe("IndexedDbStorage", () => { - const testFunc = typeof indexedDB !== "undefined" ? it : xit; + const testFunc = typeof indexedDB !== "undefined" ? it : it.skip; testFunc("should remember key-value pairs", async () => { const storage = new IndexedDbStorage(); diff --git a/packages/credential-provider-cognito-identity/src/fromCognitoIdentity.spec.ts b/packages/credential-provider-cognito-identity/src/fromCognitoIdentity.spec.ts index b2155468febc..d7ab753a7465 100644 --- a/packages/credential-provider-cognito-identity/src/fromCognitoIdentity.spec.ts +++ b/packages/credential-provider-cognito-identity/src/fromCognitoIdentity.spec.ts @@ -1,12 +1,13 @@ import { GetCredentialsForIdentityCommand } from "@aws-sdk/client-cognito-identity"; import { CredentialsProviderError } from "@smithy/property-provider"; +import { beforeEach, describe, expect, test as it, vi } from "vitest"; import { fromCognitoIdentity } from "./fromCognitoIdentity"; describe("fromCognitoIdentity", () => { const identityId = "id"; const expiration = new Date(); - const send = jest.fn().mockResolvedValue({ + const send = vi.fn().mockResolvedValue({ Credentials: { AccessKeyId: "foo", SecretKey: "bar", diff --git a/packages/credential-provider-cognito-identity/src/fromCognitoIdentityPool.spec.ts b/packages/credential-provider-cognito-identity/src/fromCognitoIdentityPool.spec.ts index 10688f37518c..aff91cdad7d7 100644 --- a/packages/credential-provider-cognito-identity/src/fromCognitoIdentityPool.spec.ts +++ b/packages/credential-provider-cognito-identity/src/fromCognitoIdentityPool.spec.ts @@ -1,25 +1,26 @@ import { GetIdCommand } from "@aws-sdk/client-cognito-identity"; import { CredentialsProviderError } from "@smithy/property-provider"; +import { beforeEach, describe, expect, test as it, vi } from "vitest"; import { fromCognitoIdentityPool } from "./fromCognitoIdentityPool"; -jest.mock("./fromCognitoIdentity", () => { - const promiseFunc = jest.fn().mockResolvedValue({ +vi.mock("./fromCognitoIdentity", () => { + const promiseFunc = vi.fn().mockResolvedValue({ accessKeyId: "foo", secretAccessKey: "bar", sessionToken: "baz", }); - return { fromCognitoIdentity: jest.fn().mockReturnValue(promiseFunc) }; + return { fromCognitoIdentity: vi.fn().mockReturnValue(promiseFunc) }; }); import { fromCognitoIdentity } from "./fromCognitoIdentity"; -jest.mock("./localStorage", () => { +vi.mock("./localStorage", () => { return { localStorage() { return { - getItem: jest.fn(), - setItem: jest.fn(), - removeItem: jest.fn(), + getItem: vi.fn(), + setItem: vi.fn(), + removeItem: vi.fn(), }; }, }; @@ -30,7 +31,7 @@ import { localStorage } from "./localStorage"; describe("fromCognitoIdentityPool", () => { const identityPoolId = "poolId"; const identityId = "id"; - const send = jest.fn().mockResolvedValue({ IdentityId: identityId }); + const send = vi.fn().mockResolvedValue({ IdentityId: identityId }); const mockClient: any = { send }; beforeEach(() => { diff --git a/packages/credential-provider-cognito-identity/src/localStorage-inmemoryStorage.spec.ts b/packages/credential-provider-cognito-identity/src/localStorage-inmemoryStorage.spec.ts index f58dca72e042..6d2710306de9 100644 --- a/packages/credential-provider-cognito-identity/src/localStorage-inmemoryStorage.spec.ts +++ b/packages/credential-provider-cognito-identity/src/localStorage-inmemoryStorage.spec.ts @@ -1,3 +1,5 @@ +import { afterEach, beforeEach, describe, expect, test as it } from "vitest"; + import { InMemoryStorage } from "./InMemoryStorage"; import { localStorage as storage } from "./localStorage"; diff --git a/packages/credential-provider-cognito-identity/src/localStorage.spec.ts b/packages/credential-provider-cognito-identity/src/localStorage.spec.ts index ff75d80537af..f71aeae8f2b1 100644 --- a/packages/credential-provider-cognito-identity/src/localStorage.spec.ts +++ b/packages/credential-provider-cognito-identity/src/localStorage.spec.ts @@ -1,3 +1,5 @@ +import { afterEach, beforeEach, describe, expect, test as it } from "vitest"; + /** * @jest-environment jsdom */ diff --git a/packages/credential-provider-cognito-identity/src/resolveLogins.spec.ts b/packages/credential-provider-cognito-identity/src/resolveLogins.spec.ts index cb09a7613b99..c7a3b6a70e90 100644 --- a/packages/credential-provider-cognito-identity/src/resolveLogins.spec.ts +++ b/packages/credential-provider-cognito-identity/src/resolveLogins.spec.ts @@ -1,3 +1,5 @@ +import { describe, expect, test as it } from "vitest"; + import { resolveLogins } from "./resolveLogins"; describe("resolveLogins", () => { diff --git a/packages/credential-provider-cognito-identity/vitest.config.ts b/packages/credential-provider-cognito-identity/vitest.config.ts new file mode 100644 index 000000000000..f360902f18aa --- /dev/null +++ b/packages/credential-provider-cognito-identity/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e,browser}.spec.{ts,js}"], + include: ["**/*.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/packages/credential-provider-env/jest.config.js b/packages/credential-provider-env/jest.config.js deleted file mode 100644 index a8d1c2e49912..000000000000 --- a/packages/credential-provider-env/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, -}; diff --git a/packages/credential-provider-env/package.json b/packages/credential-provider-env/package.json index 67e839fb5c31..bf685d16b57d 100644 --- a/packages/credential-provider-env/package.json +++ b/packages/credential-provider-env/package.json @@ -12,7 +12,8 @@ "build:types": "tsc -p tsconfig.types.json", "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", - "test": "jest" + "test": "vitest run", + "test:watch": "vitest watch" }, "keywords": [ "aws", diff --git a/packages/credential-provider-env/src/fromEnv.spec.ts b/packages/credential-provider-env/src/fromEnv.spec.ts index f75430bdca85..4be51676eef9 100644 --- a/packages/credential-provider-env/src/fromEnv.spec.ts +++ b/packages/credential-provider-env/src/fromEnv.spec.ts @@ -1,4 +1,5 @@ import { CredentialsProviderError } from "@smithy/property-provider"; +import { afterEach, beforeEach, describe, expect, test as it } from "vitest"; import { ENV_ACCOUNT_ID, ENV_EXPIRATION, ENV_KEY, ENV_SECRET, ENV_SESSION, fromEnv } from "./fromEnv"; diff --git a/packages/credential-provider-env/vitest.config.ts b/packages/credential-provider-env/vitest.config.ts new file mode 100644 index 000000000000..f360902f18aa --- /dev/null +++ b/packages/credential-provider-env/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e,browser}.spec.{ts,js}"], + include: ["**/*.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/packages/credential-provider-http/jest.config.js b/packages/credential-provider-http/jest.config.js deleted file mode 100644 index a8d1c2e49912..000000000000 --- a/packages/credential-provider-http/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, -}; diff --git a/packages/credential-provider-http/package.json b/packages/credential-provider-http/package.json index 63a3267731b8..964c09667317 100644 --- a/packages/credential-provider-http/package.json +++ b/packages/credential-provider-http/package.json @@ -14,7 +14,8 @@ "build:types": "tsc -p tsconfig.types.json", "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", - "test": "jest" + "test": "vitest run", + "test:watch": "vitest watch" }, "keywords": [ "aws", diff --git a/packages/credential-provider-http/src/fromHttp/checkUrl.spec.ts b/packages/credential-provider-http/src/fromHttp/checkUrl.spec.ts index 0e9aad1105f1..815d92ce627d 100644 --- a/packages/credential-provider-http/src/fromHttp/checkUrl.spec.ts +++ b/packages/credential-provider-http/src/fromHttp/checkUrl.spec.ts @@ -1,4 +1,5 @@ import { CredentialsProviderError } from "@smithy/property-provider"; +import { describe, expect, test as it } from "vitest"; import { checkUrl } from "./checkUrl"; diff --git a/packages/credential-provider-http/src/fromHttp/fromHttp.spec.ts b/packages/credential-provider-http/src/fromHttp/fromHttp.spec.ts index 903bb9c12d96..38584ec4e320 100644 --- a/packages/credential-provider-http/src/fromHttp/fromHttp.spec.ts +++ b/packages/credential-provider-http/src/fromHttp/fromHttp.spec.ts @@ -1,5 +1,6 @@ import { HttpResponse } from "@smithy/protocol-http"; import { Readable } from "stream"; +import { afterAll, describe, expect, test as it, vi } from "vitest"; import { fromHttp } from "./fromHttp"; import * as helpers from "./requestHelpers"; @@ -21,7 +22,7 @@ const mockResponse = { Expiration: new Date(credentials.expiration).toISOString(), // rfc3339 }; -const mockHandle = jest.fn().mockResolvedValue({ +const mockHandle = vi.fn().mockResolvedValue({ response: new HttpResponse({ statusCode: 200, headers: { @@ -31,25 +32,27 @@ const mockHandle = jest.fn().mockResolvedValue({ }), }); -jest.mock("@smithy/node-http-handler", () => ({ - NodeHttpHandler: jest.fn().mockImplementation(() => ({ +vi.mock("@smithy/node-http-handler", () => ({ + NodeHttpHandler: vi.fn().mockImplementation(() => ({ destroy: () => {}, handle: mockHandle, })), - streamCollector: jest.fn(), + streamCollector: vi.fn(), })); -jest.spyOn(helpers, "getCredentials").mockReturnValue(Promise.resolve(credentials)); +vi.spyOn(helpers, "getCredentials").mockReturnValue(Promise.resolve(credentials)); -jest.mock("fs/promises", () => ({ - async readFile() { - return mockToken; +vi.mock("fs/promises", () => ({ + default: { + async readFile() { + return mockToken; + }, }, })); describe(fromHttp.name, () => { afterAll(() => { - jest.resetAllMocks(); + vi.resetAllMocks(); }); it("uses the full uri", async () => { diff --git a/packages/credential-provider-http/src/fromHttp/requestHelpers.spec.ts b/packages/credential-provider-http/src/fromHttp/requestHelpers.spec.ts index df2f52cc0719..3feb35c429be 100644 --- a/packages/credential-provider-http/src/fromHttp/requestHelpers.spec.ts +++ b/packages/credential-provider-http/src/fromHttp/requestHelpers.spec.ts @@ -2,6 +2,7 @@ import { CredentialsProviderError } from "@smithy/property-provider"; import { HttpResponse } from "@smithy/protocol-http"; import { parseRfc3339DateTime } from "@smithy/smithy-client"; import { Readable } from "stream"; +import { describe, expect, test as it } from "vitest"; import { createGetRequest, getCredentials } from "./requestHelpers"; diff --git a/packages/credential-provider-http/vitest.config.ts b/packages/credential-provider-http/vitest.config.ts new file mode 100644 index 000000000000..f360902f18aa --- /dev/null +++ b/packages/credential-provider-http/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e,browser}.spec.{ts,js}"], + include: ["**/*.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/packages/credential-provider-ini/jest.config.js b/packages/credential-provider-ini/jest.config.js deleted file mode 100644 index a8d1c2e49912..000000000000 --- a/packages/credential-provider-ini/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, -}; diff --git a/packages/credential-provider-ini/package.json b/packages/credential-provider-ini/package.json index 39fd6812d3ad..b495a8dc73c5 100644 --- a/packages/credential-provider-ini/package.json +++ b/packages/credential-provider-ini/package.json @@ -12,7 +12,8 @@ "build:types": "tsc -p tsconfig.types.json", "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", - "test": "jest" + "test": "vitest run", + "test:watch": "vitest watch" }, "keywords": [ "aws", diff --git a/packages/credential-provider-ini/src/fromIni.spec.ts b/packages/credential-provider-ini/src/fromIni.spec.ts index 202da487bc70..d7651a18d82e 100644 --- a/packages/credential-provider-ini/src/fromIni.spec.ts +++ b/packages/credential-provider-ini/src/fromIni.spec.ts @@ -1,11 +1,12 @@ import { getProfileName, parseKnownFiles } from "@smithy/shared-ini-file-loader"; import { AwsCredentialIdentity } from "@smithy/types"; +import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest"; import { fromIni } from "./fromIni"; import { resolveProfileData } from "./resolveProfileData"; -jest.mock("@smithy/shared-ini-file-loader"); -jest.mock("./resolveProfileData"); +vi.mock("@smithy/shared-ini-file-loader"); +vi.mock("./resolveProfileData"); describe(fromIni.name, () => { const mockMasterProfileName = "mockMasterProfileName"; @@ -14,17 +15,17 @@ describe(fromIni.name, () => { const mockProfiles = { [mockProfileName]: { key: "value" } }; beforeEach(() => { - (parseKnownFiles as jest.Mock).mockResolvedValue(mockProfiles); - (getProfileName as jest.Mock).mockReturnValue(mockMasterProfileName); + vi.mocked(parseKnownFiles).mockResolvedValue(mockProfiles); + vi.mocked(getProfileName).mockReturnValue(mockMasterProfileName); }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("rethrows error if parsing known files fails", async () => { const expectedError = new Error("from parseKnownFiles"); - (parseKnownFiles as jest.Mock).mockRejectedValue(expectedError); + vi.mocked(parseKnownFiles).mockRejectedValue(expectedError); try { await fromIni(mockInit)(); fail(`expected ${expectedError}`); @@ -38,7 +39,7 @@ describe(fromIni.name, () => { it("rethrows error if resolving process creds fails", async () => { const expectedError = new Error("from resolveProcessCredentials"); - (resolveProfileData as jest.Mock).mockRejectedValue(expectedError); + vi.mocked(resolveProfileData).mockRejectedValue(expectedError); try { await fromIni(mockInit)(); fail(`expected ${expectedError}`); @@ -55,7 +56,7 @@ describe(fromIni.name, () => { accessKeyId: "mockAccessKeyId", secretAccessKey: "mockSecretAccessKey", }; - (resolveProfileData as jest.Mock).mockResolvedValue(expectedCreds); + vi.mocked(resolveProfileData).mockResolvedValue(expectedCreds); const receivedCreds = await fromIni(mockInit)(); expect(receivedCreds).toStrictEqual(expectedCreds); expect(parseKnownFiles).toHaveBeenCalledWith(mockInit); diff --git a/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.spec.ts b/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.spec.ts index b36372fd6dd5..869a0a20ec3e 100644 --- a/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.spec.ts +++ b/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.spec.ts @@ -1,18 +1,19 @@ import { CredentialsProviderError } from "@smithy/property-provider"; import { getProfileName } from "@smithy/shared-ini-file-loader"; +import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest"; import { isAssumeRoleProfile, resolveAssumeRoleCredentials } from "./resolveAssumeRoleCredentials"; import { resolveCredentialSource } from "./resolveCredentialSource"; import { resolveProfileData } from "./resolveProfileData"; -jest.mock("@aws-sdk/client-sts", () => { +vi.mock("@aws-sdk/client-sts", () => { return { - getDefaultRoleAssumer: jest.fn().mockReturnValue(async () => ({})), + getDefaultRoleAssumer: vi.fn().mockReturnValue(async () => ({})), }; }); -jest.mock("@smithy/shared-ini-file-loader"); -jest.mock("./resolveCredentialSource"); -jest.mock("./resolveProfileData"); +vi.mock("@smithy/shared-ini-file-loader"); +vi.mock("./resolveCredentialSource"); +vi.mock("./resolveProfileData"); const getMockAssumeRoleProfile = () => ({ role_arn: "mock_role_arn", @@ -97,9 +98,9 @@ describe(resolveAssumeRoleCredentials.name, () => { const mockProfileName = "mockProfileName"; const mockProfiles = { [mockProfileName]: {} }; const mockOptions = { - mfaCodeProvider: jest.fn(), - roleAssumer: jest.fn().mockReturnValue(Promise.resolve(mockCreds)), - roleAssumerWithWebIdentity: jest.fn(), + mfaCodeProvider: vi.fn(), + roleAssumer: vi.fn().mockReturnValue(Promise.resolve(mockCreds)), + roleAssumerWithWebIdentity: vi.fn(), }; const mockCredentialSource = "mockCredentialSource"; @@ -118,13 +119,13 @@ describe(resolveAssumeRoleCredentials.name, () => { }); beforeEach(() => { - (getProfileName as jest.Mock).mockReturnValue(mockProfileName); - (resolveProfileData as jest.Mock).mockResolvedValue(mockSourceCredsFromProfile); - (resolveCredentialSource as jest.Mock).mockReturnValue(async () => async () => mockSourceCredsFromCredential); + vi.mocked(getProfileName).mockReturnValue(mockProfileName); + vi.mocked(resolveProfileData).mockResolvedValue(mockSourceCredsFromProfile); + vi.mocked(resolveCredentialSource).mockReturnValue(async () => async () => mockSourceCredsFromCredential); }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("dynamically loads STS if roleAssumer is not available in options", async () => { @@ -205,7 +206,7 @@ describe(resolveAssumeRoleCredentials.name, () => { it("sets role session name if not provided", async () => { const mockDateNow = Date.now(); - jest.spyOn(Date, "now").mockReturnValue(mockDateNow); + vi.spyOn(Date, "now").mockReturnValue(mockDateNow); const mockRoleAssumeParams = { ...getMockRoleAssumeParams(), role_session_name: undefined }; const mockProfilesWithCredSource = getMockProfilesWithCredSource(mockRoleAssumeParams); diff --git a/packages/credential-provider-ini/src/resolveCredentialSource.spec.ts b/packages/credential-provider-ini/src/resolveCredentialSource.spec.ts index ce117bd7e8fe..9eac5514dbb3 100644 --- a/packages/credential-provider-ini/src/resolveCredentialSource.spec.ts +++ b/packages/credential-provider-ini/src/resolveCredentialSource.spec.ts @@ -1,6 +1,8 @@ -jest.mock("@aws-sdk/credential-provider-env"); -jest.mock("@aws-sdk/credential-provider-http"); -jest.mock("@smithy/credential-provider-imds"); +import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest"; + +vi.mock("@aws-sdk/credential-provider-env"); +vi.mock("@aws-sdk/credential-provider-http"); +vi.mock("@smithy/credential-provider-imds"); import { fromEnv } from "@aws-sdk/credential-provider-env"; import { fromHttp } from "@aws-sdk/credential-provider-http"; @@ -23,16 +25,16 @@ describe(resolveCredentialSource.name, () => { }; beforeEach(() => { - (fromEnv as jest.Mock).mockReturnValue(() => Promise.resolve(mockFakeCreds)); - (fromContainerMetadata as jest.Mock).mockReturnValue(() => Promise.resolve(mockFakeCreds)); - (fromHttp as jest.Mock).mockReturnValue(() => { + vi.mocked(fromEnv).mockReturnValue(() => Promise.resolve(mockFakeCreds)); + vi.mocked(fromContainerMetadata).mockReturnValue(() => Promise.resolve(mockFakeCreds)); + vi.mocked(fromHttp).mockReturnValue(() => { throw new CredentialsProviderError("try next", {}); }); - (fromInstanceMetadata as jest.Mock).mockReturnValue(() => Promise.resolve(mockFakeCreds)); + vi.mocked(fromInstanceMetadata).mockReturnValue(() => Promise.resolve(mockFakeCreds)); }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it.each([ @@ -40,7 +42,7 @@ describe(resolveCredentialSource.name, () => { ["Ec2InstanceMetadata", fromInstanceMetadata], ["Environment", fromEnv], ])("when credentialSource=%s, calls %p", async (credentialSource, fromFn) => { - (fromFn as jest.Mock).mockReturnValue(() => Promise.resolve(mockCreds)); + vi.mocked(fromFn).mockReturnValue(() => Promise.resolve(mockCreds)); const providerFactory = resolveCredentialSource(credentialSource, mockProfileName); expect(typeof providerFactory).toEqual("function"); diff --git a/packages/credential-provider-ini/src/resolveProcessCredentials.spec.ts b/packages/credential-provider-ini/src/resolveProcessCredentials.spec.ts index 831d5be9074c..3e40585f5918 100644 --- a/packages/credential-provider-ini/src/resolveProcessCredentials.spec.ts +++ b/packages/credential-provider-ini/src/resolveProcessCredentials.spec.ts @@ -1,9 +1,10 @@ import { fromProcess } from "@aws-sdk/credential-provider-process"; import { Credentials } from "@aws-sdk/types"; +import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest"; import { isProcessProfile, resolveProcessCredentials } from "./resolveProcessCredentials"; -jest.mock("@aws-sdk/credential-provider-process"); +vi.mock("@aws-sdk/credential-provider-process"); const getMockProcessProfile = () => ({ credential_process: "mock_command", @@ -40,11 +41,11 @@ describe(resolveProcessCredentials.name, () => { }; beforeEach(() => { - (fromProcess as jest.Mock).mockReturnValue(() => Promise.resolve(mockCreds)); + vi.mocked(fromProcess).mockReturnValue(() => Promise.resolve(mockCreds)); }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("throws error when fromProcess throws", async () => { @@ -52,7 +53,7 @@ describe(resolveProcessCredentials.name, () => { const mockOptions = {}; const expectedError = new Error("error from fromProcess"); - (fromProcess as jest.Mock).mockReturnValue(() => Promise.reject(expectedError)); + vi.mocked(fromProcess).mockReturnValue(() => Promise.reject(expectedError)); try { await resolveProcessCredentials(mockOptions, mockProfileName); @@ -69,7 +70,7 @@ describe(resolveProcessCredentials.name, () => { const mockProfileName = "mockProfileName"; const mockOptions = {}; - (fromProcess as jest.Mock).mockReturnValue(() => Promise.resolve(mockCreds)); + vi.mocked(fromProcess).mockReturnValue(() => Promise.resolve(mockCreds)); const receivedCreds = await resolveProcessCredentials(mockOptions, mockProfileName); expect(receivedCreds).toStrictEqual(mockCreds); diff --git a/packages/credential-provider-ini/src/resolveProfileData.spec.ts b/packages/credential-provider-ini/src/resolveProfileData.spec.ts index 61576b34afab..a8b3154edf6f 100644 --- a/packages/credential-provider-ini/src/resolveProfileData.spec.ts +++ b/packages/credential-provider-ini/src/resolveProfileData.spec.ts @@ -1,4 +1,5 @@ import { CredentialsProviderError } from "@smithy/property-provider"; +import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest"; import { isAssumeRoleProfile, resolveAssumeRoleCredentials } from "./resolveAssumeRoleCredentials"; import { resolveProfileData } from "./resolveProfileData"; @@ -6,18 +7,18 @@ import { isSsoProfile, resolveSsoCredentials } from "./resolveSsoCredentials"; import { isStaticCredsProfile, resolveStaticCredentials } from "./resolveStaticCredentials"; import { isWebIdentityProfile, resolveWebIdentityCredentials } from "./resolveWebIdentityCredentials"; -jest.mock("./resolveAssumeRoleCredentials"); -jest.mock("./resolveSsoCredentials"); -jest.mock("./resolveStaticCredentials"); -jest.mock("./resolveWebIdentityCredentials"); +vi.mock("./resolveAssumeRoleCredentials"); +vi.mock("./resolveSsoCredentials"); +vi.mock("./resolveStaticCredentials"); +vi.mock("./resolveWebIdentityCredentials"); describe(resolveProfileData.name, () => { const mockProfileName = "mockProfileName"; const mockProfiles = { [mockProfileName]: {} }; const mockOptions = { - mfaCodeProvider: jest.fn(), - roleAssumer: jest.fn(), - roleAssumerWithWebIdentity: jest.fn(), + mfaCodeProvider: vi.fn(), + roleAssumer: vi.fn(), + roleAssumerWithWebIdentity: vi.fn(), }; const mockError = new CredentialsProviderError( `Could not resolve credentials using profile: [${mockProfileName}] in configuration/credentials file(s).` @@ -40,23 +41,23 @@ describe(resolveProfileData.name, () => { resolveStaticCredentials, resolveWebIdentityCredentials, ].forEach((resolveCredsFn) => { - (resolveCredsFn as jest.Mock).mockImplementation(() => Promise.resolve(mockFakeCreds)); + vi.mocked(resolveCredsFn).mockImplementation(() => Promise.resolve(mockFakeCreds)); }); }); beforeEach(() => { [isAssumeRoleProfile, isSsoProfile, isStaticCredsProfile, isWebIdentityProfile].forEach((isProfileFn) => { - (isProfileFn as jest.Mock).mockReturnValue(true); + vi.mocked(isProfileFn).mockReturnValue(true); }); }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("throws error if all profile checks fail", async () => { [isAssumeRoleProfile, isSsoProfile, isStaticCredsProfile, isWebIdentityProfile].forEach((isProfileFn) => { - (isProfileFn as jest.Mock).mockReturnValue(false); + vi.mocked(isProfileFn).mockReturnValue(false); }); try { await resolveProfileData(mockProfileName, mockProfiles, mockOptions); @@ -67,7 +68,7 @@ describe(resolveProfileData.name, () => { }); it("resolves with static creds when profiles are previously visited and current profile has static creds", async () => { - (resolveStaticCredentials as jest.Mock).mockImplementation(() => Promise.resolve(mockCreds)); + vi.mocked(resolveStaticCredentials).mockImplementation(() => Promise.resolve(mockCreds)); const receivedCreds = await resolveProfileData(mockProfileName, mockProfiles, mockOptions, { testProfile: true }); expect(receivedCreds).toStrictEqual(mockCreds); expect(resolveStaticCredentials).toHaveBeenCalledWith(mockProfiles[mockProfileName], mockOptions); @@ -75,15 +76,15 @@ describe(resolveProfileData.name, () => { describe("resolves with assumeRole", () => { it("when it's static creds, but profiles are not visited", async () => { - (resolveAssumeRoleCredentials as jest.Mock).mockImplementation(() => Promise.resolve(mockCreds)); + vi.mocked(resolveAssumeRoleCredentials).mockImplementation(() => Promise.resolve(mockCreds)); const receivedCreds = await resolveProfileData(mockProfileName, mockProfiles, mockOptions); expect(receivedCreds).toStrictEqual(mockCreds); expect(resolveAssumeRoleCredentials).toHaveBeenCalledWith(mockProfileName, mockProfiles, mockOptions, {}); }); it("when it's not static creds, and profiles are visited", async () => { - (isStaticCredsProfile as unknown as jest.Mock).mockReturnValue(false); - (resolveAssumeRoleCredentials as jest.Mock).mockImplementation(() => Promise.resolve(mockCreds)); + (isStaticCredsProfile as unknown as any).mockReturnValue(false); + vi.mocked(resolveAssumeRoleCredentials).mockImplementation(() => Promise.resolve(mockCreds)); const receivedCreds = await resolveProfileData(mockProfileName, mockProfiles, mockOptions, { testProfile: true }); expect(receivedCreds).toStrictEqual(mockCreds); expect(resolveAssumeRoleCredentials).toHaveBeenCalledWith(mockProfileName, mockProfiles, mockOptions, { @@ -93,8 +94,8 @@ describe(resolveProfileData.name, () => { }); it("resolves with static creds when no profiles are visited and it's not assume role profile", async () => { - (isAssumeRoleProfile as unknown as jest.Mock).mockReturnValue(false); - (resolveStaticCredentials as jest.Mock).mockImplementation(() => Promise.resolve(mockCreds)); + (isAssumeRoleProfile as unknown as any).mockReturnValue(false); + vi.mocked(resolveStaticCredentials).mockImplementation(() => Promise.resolve(mockCreds)); const receivedCreds = await resolveProfileData(mockProfileName, mockProfiles, mockOptions); expect(receivedCreds).toStrictEqual(mockCreds); expect(resolveStaticCredentials).toHaveBeenCalledWith(mockProfiles[mockProfileName], mockOptions); @@ -102,9 +103,9 @@ describe(resolveProfileData.name, () => { it("resolves with web identity profile, when it's not static or assume role", async () => { [isAssumeRoleProfile, isStaticCredsProfile].forEach((isProfileFn) => { - (isProfileFn as jest.Mock).mockReturnValue(false); + vi.mocked(isProfileFn).mockReturnValue(false); }); - (resolveWebIdentityCredentials as jest.Mock).mockImplementation(() => Promise.resolve(mockCreds)); + vi.mocked(resolveWebIdentityCredentials).mockImplementation(() => Promise.resolve(mockCreds)); const receivedCreds = await resolveProfileData(mockProfileName, mockProfiles, mockOptions); expect(receivedCreds).toStrictEqual(mockCreds); expect(resolveWebIdentityCredentials).toHaveBeenCalledWith(mockProfiles[mockProfileName], mockOptions); @@ -112,9 +113,9 @@ describe(resolveProfileData.name, () => { it("resolves with sso profile, when it's not static or assume role or web identity", async () => { [isAssumeRoleProfile, isStaticCredsProfile, isWebIdentityProfile].forEach((isProfileFn) => { - (isProfileFn as jest.Mock).mockReturnValue(false); + vi.mocked(isProfileFn).mockReturnValue(false); }); - (resolveSsoCredentials as jest.Mock).mockImplementation(() => Promise.resolve(mockCreds)); + vi.mocked(resolveSsoCredentials).mockImplementation(() => Promise.resolve(mockCreds)); const receivedCreds = await resolveProfileData(mockProfileName, mockProfiles, mockOptions); expect(receivedCreds).toStrictEqual(mockCreds); expect(resolveSsoCredentials).toHaveBeenCalledWith(mockProfileName, {}, mockOptions); diff --git a/packages/credential-provider-ini/src/resolveSsoCredentials.spec.ts b/packages/credential-provider-ini/src/resolveSsoCredentials.spec.ts index 51f2770c672f..2953cb51fce0 100644 --- a/packages/credential-provider-ini/src/resolveSsoCredentials.spec.ts +++ b/packages/credential-provider-ini/src/resolveSsoCredentials.spec.ts @@ -1,9 +1,10 @@ import { fromSSO } from "@aws-sdk/credential-provider-sso"; import { AwsCredentialIdentity } from "@smithy/types"; +import { afterEach, describe, expect, test as it, vi } from "vitest"; import { isSsoProfile, resolveSsoCredentials } from "./resolveSsoCredentials"; -jest.mock("@aws-sdk/credential-provider-sso"); +vi.mock("@aws-sdk/credential-provider-sso"); describe(isSsoProfile.name, () => { it("returns false for empty profile", () => { @@ -20,14 +21,14 @@ describe(isSsoProfile.name, () => { describe(resolveSsoCredentials.name, () => { afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("throws error when fromSSO throws error", async () => { const mockProfileName = "mockProfileName"; const expectedError = new Error("error from fromSSO"); - (fromSSO as jest.Mock).mockReturnValue(() => Promise.reject(expectedError)); + vi.mocked(fromSSO).mockReturnValue(() => Promise.reject(expectedError)); try { await resolveSsoCredentials(mockProfileName, {}); @@ -47,7 +48,7 @@ describe(resolveSsoCredentials.name, () => { secretAccessKey: "mockSecretAccessKey", }; - (fromSSO as jest.Mock).mockReturnValue(() => Promise.resolve(mockCreds)); + vi.mocked(fromSSO).mockReturnValue(() => Promise.resolve(mockCreds)); const receivedCreds = await resolveSsoCredentials(mockProfileName, {}); expect(receivedCreds).toStrictEqual(mockCreds); diff --git a/packages/credential-provider-ini/src/resolveStaticCredentials.spec.ts b/packages/credential-provider-ini/src/resolveStaticCredentials.spec.ts index c927e07f30ab..16a3e3de7078 100644 --- a/packages/credential-provider-ini/src/resolveStaticCredentials.spec.ts +++ b/packages/credential-provider-ini/src/resolveStaticCredentials.spec.ts @@ -1,3 +1,5 @@ +import { describe, expect, test as it } from "vitest"; + import { isStaticCredsProfile, resolveStaticCredentials } from "./resolveStaticCredentials"; const getMockStaticCredsProfile = () => ({ diff --git a/packages/credential-provider-ini/src/resolveWebIdentityCredentials.spec.ts b/packages/credential-provider-ini/src/resolveWebIdentityCredentials.spec.ts index a44cf5da4ace..f7b47923ef1b 100644 --- a/packages/credential-provider-ini/src/resolveWebIdentityCredentials.spec.ts +++ b/packages/credential-provider-ini/src/resolveWebIdentityCredentials.spec.ts @@ -1,9 +1,10 @@ import { fromTokenFile } from "@aws-sdk/credential-provider-web-identity"; import { AwsCredentialIdentity } from "@smithy/types"; +import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest"; import { isWebIdentityProfile, resolveWebIdentityCredentials } from "./resolveWebIdentityCredentials"; -jest.mock("@aws-sdk/credential-provider-web-identity"); +vi.mock("@aws-sdk/credential-provider-web-identity"); const getMockWebIdentityProfile = () => ({ web_identity_token_file: "mock_web_identity_token_file", @@ -48,19 +49,19 @@ describe(resolveWebIdentityCredentials.name, () => { }; beforeEach(() => { - (fromTokenFile as jest.Mock).mockReturnValue(() => Promise.resolve(mockCreds)); + vi.mocked(fromTokenFile).mockReturnValue(() => Promise.resolve(mockCreds)); }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("throws error when fromTokenFile throws", async () => { const mockProfile = getMockWebIdentityProfile(); - const mockOptions = { roleAssumerWithWebIdentity: jest.fn() }; + const mockOptions = { roleAssumerWithWebIdentity: vi.fn() }; const expectedError = new Error("error from fromTokenFile"); - (fromTokenFile as jest.Mock).mockReturnValue(() => Promise.reject(expectedError)); + vi.mocked(fromTokenFile).mockReturnValue(() => Promise.reject(expectedError)); try { await resolveWebIdentityCredentials(mockProfile, mockOptions); @@ -78,9 +79,9 @@ describe(resolveWebIdentityCredentials.name, () => { it("returns creds from fromTokenFile", async () => { const mockProfile = getMockWebIdentityProfile(); - const mockOptions = { roleAssumerWithWebIdentity: jest.fn() }; + const mockOptions = { roleAssumerWithWebIdentity: vi.fn() }; - (fromTokenFile as jest.Mock).mockReturnValue(() => Promise.resolve(mockCreds)); + vi.mocked(fromTokenFile).mockReturnValue(() => Promise.resolve(mockCreds)); const receivedCreds = await resolveWebIdentityCredentials(mockProfile, mockOptions); expect(receivedCreds).toStrictEqual(mockCreds); diff --git a/packages/credential-provider-ini/vitest.config.ts b/packages/credential-provider-ini/vitest.config.ts new file mode 100644 index 000000000000..f360902f18aa --- /dev/null +++ b/packages/credential-provider-ini/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e,browser}.spec.{ts,js}"], + include: ["**/*.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/packages/credential-provider-node/jest.config.js b/packages/credential-provider-node/jest.config.js deleted file mode 100644 index a8d1c2e49912..000000000000 --- a/packages/credential-provider-node/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, -}; diff --git a/packages/credential-provider-node/package.json b/packages/credential-provider-node/package.json index f73f64b65b7e..dfcb7f87da33 100644 --- a/packages/credential-provider-node/package.json +++ b/packages/credential-provider-node/package.json @@ -15,8 +15,9 @@ "build:types": "tsc -p tsconfig.types.json", "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", - "test": "jest", - "test:integration": "jest -c jest.config.integ.js" + "test": "vitest run", + "test:integration": "jest -c jest.config.integ.js", + "test:watch": "vitest watch" }, "keywords": [ "aws", diff --git a/packages/credential-provider-node/src/defaultProvider.spec.ts b/packages/credential-provider-node/src/defaultProvider.spec.ts index ceaff2aa4def..c078787148b1 100644 --- a/packages/credential-provider-node/src/defaultProvider.spec.ts +++ b/packages/credential-provider-node/src/defaultProvider.spec.ts @@ -5,18 +5,19 @@ import { fromSSO } from "@aws-sdk/credential-provider-sso"; import { fromTokenFile } from "@aws-sdk/credential-provider-web-identity"; import { CredentialsProviderError } from "@smithy/property-provider"; import { ENV_PROFILE, loadSharedConfigFiles } from "@smithy/shared-ini-file-loader"; +import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest"; import { credentialsTreatedAsExpired, credentialsWillNeedRefresh, defaultProvider } from "./defaultProvider"; import { remoteProvider } from "./remoteProvider"; -jest.mock("@aws-sdk/credential-provider-env"); -jest.mock("@smithy/credential-provider-imds"); -jest.mock("@aws-sdk/credential-provider-ini"); -jest.mock("@aws-sdk/credential-provider-process"); -jest.mock("@aws-sdk/credential-provider-sso"); -jest.mock("@aws-sdk/credential-provider-web-identity"); -jest.mock("@smithy/shared-ini-file-loader"); -jest.mock("./remoteProvider"); +vi.mock("@aws-sdk/credential-provider-env"); +vi.mock("@smithy/credential-provider-imds"); +vi.mock("@aws-sdk/credential-provider-ini"); +vi.mock("@aws-sdk/credential-provider-process"); +vi.mock("@aws-sdk/credential-provider-sso"); +vi.mock("@aws-sdk/credential-provider-web-identity"); +vi.mock("@smithy/shared-ini-file-loader"); +vi.mock("./remoteProvider"); describe(defaultProvider.name, () => { const mockCreds = { @@ -42,12 +43,12 @@ describe(defaultProvider.name, () => { }, }; - const mockEnvFn = jest.fn().mockImplementation(() => credentials()); - const mockSsoFn = jest.fn().mockImplementation(() => credentials()); - const mockIniFn = jest.fn().mockImplementation(() => credentials()); - const mockProcessFn = jest.fn().mockImplementation(() => credentials()); - const mockTokenFileFn = jest.fn().mockImplementation(() => credentials()); - const mockRemoteProviderFn = jest.fn().mockImplementation(() => finalCredentials()); + const mockEnvFn = vi.fn().mockImplementation(() => credentials()); + const mockSsoFn = vi.fn().mockImplementation(() => credentials()); + const mockIniFn = vi.fn().mockImplementation(() => credentials()); + const mockProcessFn = vi.fn().mockImplementation(() => credentials()); + const mockTokenFileFn = vi.fn().mockImplementation(() => credentials()); + const mockRemoteProviderFn = vi.fn().mockImplementation(() => finalCredentials()); const ORIGINAL_ENV = { ...process.env, @@ -68,12 +69,12 @@ describe(defaultProvider.name, () => { [fromTokenFile, mockTokenFileFn], [remoteProvider, mockRemoteProviderFn], ].forEach(([fromFn, mockFn]) => { - (fromFn as jest.Mock).mockReturnValue(mockFn); + vi.mocked(fromFn).mockReturnValue(mockFn); }); }); afterEach(async () => { - jest.clearAllMocks(); + vi.clearAllMocks(); process.env = ORIGINAL_ENV; }); @@ -96,7 +97,7 @@ describe(defaultProvider.name, () => { expect(loadSharedConfigFiles).not.toHaveBeenCalled(); } - jest.clearAllMocks(); + vi.clearAllMocks(); // subsequent call does not enter the chain. { @@ -154,7 +155,7 @@ describe(defaultProvider.name, () => { describe(credentialsTreatedAsExpired.name, () => { const mockDateNow = Date.now(); beforeEach(async () => { - jest.spyOn(Date, "now").mockReturnValueOnce(mockDateNow); + vi.spyOn(Date, "now").mockReturnValueOnce(mockDateNow); }); it("returns true if expiration is defined, and creds have expired", () => { diff --git a/packages/credential-provider-node/src/remoteProvider.spec.ts b/packages/credential-provider-node/src/remoteProvider.spec.ts index de91e24e1dea..87fabfd69dd7 100644 --- a/packages/credential-provider-node/src/remoteProvider.spec.ts +++ b/packages/credential-provider-node/src/remoteProvider.spec.ts @@ -5,27 +5,34 @@ import { fromInstanceMetadata, } from "@smithy/credential-provider-imds"; import { CredentialsProviderError } from "@smithy/property-provider"; +import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest"; import { ENV_IMDS_DISABLED, remoteProvider } from "./remoteProvider"; -jest.mock("@smithy/credential-provider-imds"); +vi.mock("@smithy/credential-provider-imds"); -describe(remoteProvider.name, () => { - const ORIGINAL_ENV = process.env; - const mockInit = { timeout: 10000, maxRetries: 5 }; - const mockCredsFromContainer = { - accessKeyId: "mockContainerAccessKeyId", - secretAccessKey: "mockContainerSecretAccessKey", - }; - const mockSourceCredsFromInstanceMetadata = { - accessKeyId: "mockInstanceMetadataAccessKeyId", - secretAccessKey: "mockInstanceMetadataSecretAccessKey", - }; - const mockFromHttp = jest.fn().mockReturnValue(async () => mockCredsFromContainer); +const ORIGINAL_ENV = process.env; +const mockInit = { timeout: 10000, maxRetries: 5 }; +const mockCredsFromContainer = { + accessKeyId: "mockContainerAccessKeyId", + secretAccessKey: "mockContainerSecretAccessKey", +}; +const mockSourceCredsFromInstanceMetadata = { + accessKeyId: "mockInstanceMetadataAccessKeyId", + secretAccessKey: "mockInstanceMetadataSecretAccessKey", +}; +const mockFromHttp = vi.fn().mockReturnValue(async () => mockCredsFromContainer); - const sampleFullUri = "http://localhost"; - const sampleRelativeUri = "/"; +const sampleFullUri = "http://localhost"; +const sampleRelativeUri = "/"; +vi.mock("@aws-sdk/credential-provider-http", () => ({ + fromHttp: mockFromHttp, + default: { + fromHttp: mockFromHttp, + }, +})); +describe(remoteProvider.name, () => { beforeEach(() => { process.env = { ...ORIGINAL_ENV, @@ -34,16 +41,13 @@ describe(remoteProvider.name, () => { [ENV_IMDS_DISABLED]: undefined, }; - jest.mock("@aws-sdk/credential-provider-http", () => ({ - fromHttp: mockFromHttp, - })); - (fromContainerMetadata as jest.Mock).mockReturnValue(async () => mockCredsFromContainer); - (fromInstanceMetadata as jest.Mock).mockReturnValue(async () => mockSourceCredsFromInstanceMetadata); + vi.mocked(fromContainerMetadata).mockReturnValue(async () => mockCredsFromContainer); + vi.mocked(fromInstanceMetadata).mockReturnValue(async () => mockSourceCredsFromInstanceMetadata); }); afterEach(() => { process.env = ORIGINAL_ENV; - jest.clearAllMocks(); + vi.clearAllMocks(); }); it(`returns fromContainerMetadata if env[${ENV_CMDS_RELATIVE_URI}] is set`, async () => { diff --git a/packages/credential-provider-node/vitest.config.integ.ts b/packages/credential-provider-node/vitest.config.integ.ts new file mode 100644 index 000000000000..35e2b17c4f3d --- /dev/null +++ b/packages/credential-provider-node/vitest.config.integ.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["**/*.integ.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/packages/credential-provider-node/vitest.config.ts b/packages/credential-provider-node/vitest.config.ts new file mode 100644 index 000000000000..f360902f18aa --- /dev/null +++ b/packages/credential-provider-node/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e,browser}.spec.{ts,js}"], + include: ["**/*.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/packages/credential-provider-process/jest.config.js b/packages/credential-provider-process/jest.config.js deleted file mode 100644 index a8d1c2e49912..000000000000 --- a/packages/credential-provider-process/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, -}; diff --git a/packages/credential-provider-process/package.json b/packages/credential-provider-process/package.json index 3efc48b45c1a..b888a961764b 100644 --- a/packages/credential-provider-process/package.json +++ b/packages/credential-provider-process/package.json @@ -12,7 +12,8 @@ "build:types": "tsc -p tsconfig.types.json", "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", - "test": "jest" + "test": "vitest run", + "test:watch": "vitest watch" }, "keywords": [ "aws", diff --git a/packages/credential-provider-process/src/fromProcess.spec.ts b/packages/credential-provider-process/src/fromProcess.spec.ts index b61e39dfa8e7..b898909d6e8f 100644 --- a/packages/credential-provider-process/src/fromProcess.spec.ts +++ b/packages/credential-provider-process/src/fromProcess.spec.ts @@ -1,11 +1,12 @@ import { getProfileName, parseKnownFiles } from "@smithy/shared-ini-file-loader"; import { AwsCredentialIdentity } from "@smithy/types"; +import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest"; import { fromProcess } from "./fromProcess"; import { resolveProcessCredentials } from "./resolveProcessCredentials"; -jest.mock("@smithy/shared-ini-file-loader"); -jest.mock("./resolveProcessCredentials"); +vi.mock("@smithy/shared-ini-file-loader"); +vi.mock("./resolveProcessCredentials"); describe(fromProcess.name, () => { const mockMasterProfileName = "mockMasterProfileName"; @@ -14,17 +15,17 @@ describe(fromProcess.name, () => { const mockProfiles = { [mockProfileName]: { key: "value" } }; beforeEach(() => { - (parseKnownFiles as jest.Mock).mockResolvedValue(mockProfiles); - (getProfileName as jest.Mock).mockReturnValue(mockMasterProfileName); + vi.mocked(parseKnownFiles).mockResolvedValue(mockProfiles); + vi.mocked(getProfileName).mockReturnValue(mockMasterProfileName); }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("rethrows error if parsing known files fails", async () => { const expectedError = new Error("from parseKnownFiles"); - (parseKnownFiles as jest.Mock).mockRejectedValue(expectedError); + vi.mocked(parseKnownFiles).mockRejectedValue(expectedError); try { await fromProcess(mockInit)(); fail(`expected ${expectedError}`); @@ -38,7 +39,7 @@ describe(fromProcess.name, () => { it("rethrows error if resolving process creds fails", async () => { const expectedError = new Error("from resolveProcessCredentials"); - (resolveProcessCredentials as jest.Mock).mockRejectedValue(expectedError); + vi.mocked(resolveProcessCredentials).mockRejectedValue(expectedError); try { await fromProcess(mockInit)(); fail(`expected ${expectedError}`); @@ -55,7 +56,7 @@ describe(fromProcess.name, () => { accessKeyId: "mockAccessKeyId", secretAccessKey: "mockSecretAccessKey", }; - (resolveProcessCredentials as jest.Mock).mockResolvedValue(expectedCreds); + vi.mocked(resolveProcessCredentials).mockResolvedValue(expectedCreds); const receivedCreds = await fromProcess(mockInit)(); expect(receivedCreds).toStrictEqual(expectedCreds); expect(parseKnownFiles).toHaveBeenCalledWith(mockInit); diff --git a/packages/credential-provider-process/src/getValidatedProcessCredentials.spec.ts b/packages/credential-provider-process/src/getValidatedProcessCredentials.spec.ts index 73dbfe23990b..4b33afa1ca46 100644 --- a/packages/credential-provider-process/src/getValidatedProcessCredentials.spec.ts +++ b/packages/credential-provider-process/src/getValidatedProcessCredentials.spec.ts @@ -1,5 +1,6 @@ import { AttributedAwsCredentialIdentity } from "@aws-sdk/types"; import { ParsedIniData } from "@smithy/types"; +import { describe, expect, test as it } from "vitest"; import { getValidatedProcessCredentials } from "./getValidatedProcessCredentials"; import { ProcessCredentials } from "./ProcessCredentials"; diff --git a/packages/credential-provider-process/src/resolveProcessCredentials.spec.ts b/packages/credential-provider-process/src/resolveProcessCredentials.spec.ts index 73915255581e..d8ee399d965b 100644 --- a/packages/credential-provider-process/src/resolveProcessCredentials.spec.ts +++ b/packages/credential-provider-process/src/resolveProcessCredentials.spec.ts @@ -1,15 +1,16 @@ import { CredentialsProviderError } from "@smithy/property-provider"; import { AwsCredentialIdentity } from "@smithy/types"; import { promisify } from "util"; +import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest"; import { getValidatedProcessCredentials } from "./getValidatedProcessCredentials"; import { resolveProcessCredentials } from "./resolveProcessCredentials"; -jest.mock("util"); -jest.mock("./getValidatedProcessCredentials"); +vi.mock("util"); +vi.mock("./getValidatedProcessCredentials"); describe(resolveProcessCredentials.name, () => { - const mockExecPromise = jest.fn(); + const mockExecPromise = vi.fn(); const mockExecPromiseOutput = `{"mockCreds":true}`; const mockProfileName = "mockProfileName"; const mockCredentialProcess = "/mock_cred_proc"; @@ -22,11 +23,11 @@ describe(resolveProcessCredentials.name, () => { beforeEach(() => { mockExecPromise.mockResolvedValue({ stdout: mockExecPromiseOutput }); - (promisify as unknown as jest.Mock).mockReturnValue(mockExecPromise); + (promisify as unknown as any).mockReturnValue(mockExecPromise); }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("throws Error if profileName is not available", async () => { @@ -90,7 +91,7 @@ describe(resolveProcessCredentials.name, () => { it("throws Error if getValidatedProcessCredentials throws", async () => { const expectedError = new Error("error from getValidatedProcessCredentials"); - (getValidatedProcessCredentials as jest.Mock).mockRejectedValue(expectedError); + vi.mocked(getValidatedProcessCredentials).mockRejectedValue(expectedError); try { await resolveProcessCredentials(mockProfileName, getMockProfiles()); @@ -111,7 +112,7 @@ describe(resolveProcessCredentials.name, () => { accessKeyId: "mockAccessKeyId", secretAccessKey: "mockSecretAccessKey", }; - (getValidatedProcessCredentials as jest.Mock).mockResolvedValue(expectedCreds); + vi.mocked(getValidatedProcessCredentials).mockResolvedValue(expectedCreds); const receivedCreds = await resolveProcessCredentials(mockProfileName, getMockProfiles()); expect(receivedCreds).toStrictEqual(expectedCreds); expect(mockExecPromise).toHaveBeenCalledWith(mockCredentialProcess); diff --git a/packages/credential-provider-process/vitest.config.ts b/packages/credential-provider-process/vitest.config.ts new file mode 100644 index 000000000000..f360902f18aa --- /dev/null +++ b/packages/credential-provider-process/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e,browser}.spec.{ts,js}"], + include: ["**/*.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/packages/credential-provider-sso/jest.config.js b/packages/credential-provider-sso/jest.config.js deleted file mode 100644 index a8d1c2e49912..000000000000 --- a/packages/credential-provider-sso/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, -}; diff --git a/packages/credential-provider-sso/package.json b/packages/credential-provider-sso/package.json index e530b3d1df7f..f550eb376b23 100644 --- a/packages/credential-provider-sso/package.json +++ b/packages/credential-provider-sso/package.json @@ -12,7 +12,8 @@ "build:types": "tsc -p tsconfig.types.json", "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", - "test": "jest" + "test": "vitest run", + "test:watch": "vitest watch" }, "keywords": [ "aws", diff --git a/packages/credential-provider-sso/src/fromSSO.spec.ts b/packages/credential-provider-sso/src/fromSSO.spec.ts index 537f93dc3ea7..be949e78f349 100644 --- a/packages/credential-provider-sso/src/fromSSO.spec.ts +++ b/packages/credential-provider-sso/src/fromSSO.spec.ts @@ -1,19 +1,20 @@ import { SSOClient } from "@aws-sdk/client-sso"; import { CredentialsProviderError } from "@smithy/property-provider"; import { getProfileName, parseKnownFiles } from "@smithy/shared-ini-file-loader"; +import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest"; import { fromSSO } from "./fromSSO"; import { isSsoProfile } from "./isSsoProfile"; import { resolveSSOCredentials } from "./resolveSSOCredentials"; import { validateSsoProfile } from "./validateSsoProfile"; -jest.mock("@smithy/shared-ini-file-loader"); -jest.mock("@aws-sdk/client-sso", () => ({ - SSOClient: jest.fn(), +vi.mock("@smithy/shared-ini-file-loader"); +vi.mock("@aws-sdk/client-sso", () => ({ + SSOClient: vi.fn(), })); -jest.mock("./isSsoProfile"); -jest.mock("./resolveSSOCredentials"); -jest.mock("./validateSsoProfile"); +vi.mock("./isSsoProfile"); +vi.mock("./resolveSSOCredentials"); +vi.mock("./validateSsoProfile"); describe(fromSSO.name, () => { const mockSsoClient = {} as SSOClient; @@ -33,18 +34,18 @@ describe(fromSSO.name, () => { const mockProfileName = "mockProfileName"; beforeEach(() => { - (resolveSSOCredentials as jest.Mock).mockResolvedValue(mockCreds); + vi.mocked(resolveSSOCredentials).mockResolvedValue(mockCreds); }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("throws error if profile is not found", async () => { const mockInit = { profile: mockProfileName }; const mockProfiles = {}; - (parseKnownFiles as jest.Mock).mockResolvedValue(mockProfiles); - (getProfileName as jest.Mock).mockReturnValue(mockProfileName); + vi.mocked(parseKnownFiles).mockResolvedValue(mockProfiles); + vi.mocked(getProfileName).mockReturnValue(mockProfileName); const expectedError = new CredentialsProviderError(`Profile ${mockProfileName} was not found.`); try { @@ -61,9 +62,9 @@ describe(fromSSO.name, () => { const mockProfiles = { [mockProfileName]: mockSsoProfile }; beforeEach(() => { - (parseKnownFiles as jest.Mock).mockResolvedValue(mockProfiles); - (getProfileName as jest.Mock).mockReturnValue(mockProfileName); - (isSsoProfile as unknown as jest.Mock).mockReturnValue(true); + vi.mocked(parseKnownFiles).mockResolvedValue(mockProfiles); + vi.mocked(getProfileName).mockReturnValue(mockProfileName); + (isSsoProfile as unknown as any).mockReturnValue(true); }); afterEach(() => { @@ -73,7 +74,7 @@ describe(fromSSO.name, () => { }); it("throws error if profile is not an Sso Profile", async () => { - (isSsoProfile as unknown as jest.Mock).mockReturnValue(false); + (isSsoProfile as unknown as any).mockReturnValue(false); const expectedError = new CredentialsProviderError( `Profile ${mockProfileName} is not configured with SSO credentials.` ); @@ -88,7 +89,7 @@ describe(fromSSO.name, () => { it("throws error if Sso Profile validation fails", async () => { const expectedError = new Error("error"); - (validateSsoProfile as jest.Mock).mockImplementation(() => { + vi.mocked(validateSsoProfile).mockImplementation(() => { throw expectedError; }); @@ -108,7 +109,7 @@ describe(fromSSO.name, () => { sso_region: "mock_sso_region", sso_role_name: "mock_sso_role_name", }; - (validateSsoProfile as jest.Mock).mockReturnValue(mockValidatedSsoProfile); + vi.mocked(validateSsoProfile).mockReturnValue(mockValidatedSsoProfile); const receivedCreds = await fromSSO(mockInit)(); expect(receivedCreds).toStrictEqual(mockCreds); diff --git a/packages/credential-provider-sso/src/isSsoProfile.spec.ts b/packages/credential-provider-sso/src/isSsoProfile.spec.ts index 8640caf9d049..88615441dd9c 100644 --- a/packages/credential-provider-sso/src/isSsoProfile.spec.ts +++ b/packages/credential-provider-sso/src/isSsoProfile.spec.ts @@ -1,3 +1,5 @@ +import { describe, expect, test as it } from "vitest"; + import { isSsoProfile } from "./isSsoProfile"; describe(isSsoProfile.name, () => { diff --git a/packages/credential-provider-sso/src/resolveSSOCredentials.spec.ts b/packages/credential-provider-sso/src/resolveSSOCredentials.spec.ts index b2ad2ff8c28c..6f1628b6a10a 100644 --- a/packages/credential-provider-sso/src/resolveSSOCredentials.spec.ts +++ b/packages/credential-provider-sso/src/resolveSSOCredentials.spec.ts @@ -2,14 +2,15 @@ import { GetRoleCredentialsCommand, SSOClient } from "@aws-sdk/client-sso"; import * as tokenProviders from "@aws-sdk/token-providers"; import { CredentialsProviderError } from "@smithy/property-provider"; import { getSSOTokenFromFile } from "@smithy/shared-ini-file-loader"; +import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest"; import { resolveSSOCredentials } from "./resolveSSOCredentials"; -jest.mock("@smithy/shared-ini-file-loader"); -jest.mock("@aws-sdk/client-sso"); -jest.mock("@aws-sdk/token-providers", () => { +vi.mock("@smithy/shared-ini-file-loader"); +vi.mock("@aws-sdk/client-sso"); +vi.mock("@aws-sdk/token-providers", () => { return { - fromSso: jest.fn(() => async () => { + fromSso: vi.fn(() => async () => { return { token: "mockAccessToken", expiration: new Date(Date.now() + 6_000_000), @@ -27,7 +28,7 @@ describe(resolveSSOCredentials.name, () => { const SHOULD_FAIL_CREDENTIAL_CHAIN = false; const refreshMessage = `To refresh this SSO session run aws sso login with the corresponding profile.`; - const mockSsoSend = jest.fn(); + const mockSsoSend = vi.fn(); const mockSsoClient = { send: mockSsoSend }; const mockOptions = { ssoStartUrl: "mock_sso_start_url", @@ -46,12 +47,12 @@ describe(resolveSSOCredentials.name, () => { }; beforeEach(() => { - (getSSOTokenFromFile as jest.Mock).mockResolvedValue(mockToken); + vi.mocked(getSSOTokenFromFile).mockResolvedValue(mockToken); mockSsoSend.mockResolvedValue({ roleCredentials: mockCreds }); }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("throws error if getSSOTokenFromFile fails", async () => { @@ -59,7 +60,7 @@ describe(resolveSSOCredentials.name, () => { `The SSO session associated with this profile is invalid. ${refreshMessage}`, SHOULD_FAIL_CREDENTIAL_CHAIN ); - (getSSOTokenFromFile as jest.Mock).mockRejectedValue(new Error("error")); + vi.mocked(getSSOTokenFromFile).mockRejectedValue(new Error("error")); try { await resolveSSOCredentials(mockOptions); @@ -96,7 +97,7 @@ describe(resolveSSOCredentials.name, () => { it("throws error if SSO session has expired", async () => { const mockExpiredToken = { ...mockToken, expiresAt: new Date(Date.now() - 60 * 1000).toISOString() }; - (getSSOTokenFromFile as jest.Mock).mockResolvedValue(mockExpiredToken); + vi.mocked(getSSOTokenFromFile).mockResolvedValue(mockExpiredToken); }); }); @@ -158,8 +159,8 @@ describe(resolveSSOCredentials.name, () => { }); it("creates SSO client with provided region, if client is not passed", async () => { - const mockCustomSsoSend = jest.fn().mockResolvedValue({ roleCredentials: mockCreds }); - (SSOClient as jest.Mock).mockReturnValue({ send: mockCustomSsoSend }); + const mockCustomSsoSend = vi.fn().mockResolvedValue({ roleCredentials: mockCreds }); + vi.mocked(SSOClient).mockReturnValue({ send: mockCustomSsoSend }); await resolveSSOCredentials({ ...mockOptions, ssoClient: undefined }); expect(mockCustomSsoSend).toHaveBeenCalledTimes(1); @@ -174,8 +175,8 @@ describe(resolveSSOCredentials.name, () => { }); it("creates SSO client with provided region, if client is not passed, and includes accountId", async () => { - const mockCustomSsoSend = jest.fn().mockResolvedValue({ roleCredentials: mockCreds }); - (SSOClient as jest.Mock).mockReturnValue({ send: mockCustomSsoSend }); + const mockCustomSsoSend = vi.fn().mockResolvedValue({ roleCredentials: mockCreds }); + vi.mocked(SSOClient).mockReturnValue({ send: mockCustomSsoSend }); const result = await resolveSSOCredentials({ ...mockOptions, ssoClient: undefined }); expect(result).toHaveProperty("accountId", mockOptions.ssoAccountId); diff --git a/packages/credential-provider-sso/src/validateSsoProfile.spec.ts b/packages/credential-provider-sso/src/validateSsoProfile.spec.ts index 562ab8d78367..8d497ef04d08 100644 --- a/packages/credential-provider-sso/src/validateSsoProfile.spec.ts +++ b/packages/credential-provider-sso/src/validateSsoProfile.spec.ts @@ -1,4 +1,5 @@ import { CredentialsProviderError } from "@smithy/property-provider"; +import { describe, expect, test as it } from "vitest"; import { validateSsoProfile } from "./validateSsoProfile"; diff --git a/packages/credential-provider-sso/vitest.config.ts b/packages/credential-provider-sso/vitest.config.ts new file mode 100644 index 000000000000..f360902f18aa --- /dev/null +++ b/packages/credential-provider-sso/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e,browser}.spec.{ts,js}"], + include: ["**/*.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/packages/credential-provider-web-identity/jest.config.js b/packages/credential-provider-web-identity/jest.config.js deleted file mode 100644 index a8d1c2e49912..000000000000 --- a/packages/credential-provider-web-identity/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, -}; diff --git a/packages/credential-provider-web-identity/package.json b/packages/credential-provider-web-identity/package.json index cb1a843ff04f..3afc5a4d05c8 100644 --- a/packages/credential-provider-web-identity/package.json +++ b/packages/credential-provider-web-identity/package.json @@ -12,7 +12,8 @@ "build:types": "tsc -p tsconfig.types.json", "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", - "test": "jest" + "test": "vitest run", + "test:watch": "vitest watch" }, "browser": { "./dist-es/fromTokenFile": false, diff --git a/packages/credential-provider-web-identity/src/fromTokenFile.spec.ts b/packages/credential-provider-web-identity/src/fromTokenFile.spec.ts index 0f4695bd3e20..e002ff7516f3 100644 --- a/packages/credential-provider-web-identity/src/fromTokenFile.spec.ts +++ b/packages/credential-provider-web-identity/src/fromTokenFile.spec.ts @@ -1,4 +1,5 @@ import { readFileSync } from "fs"; +import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest"; import { fromTokenFile } from "./fromTokenFile"; import { fromWebToken } from "./fromWebToken"; @@ -9,8 +10,8 @@ const ENV_ROLE_SESSION_NAME = "AWS_ROLE_SESSION_NAME"; import { CredentialsProviderError } from "@smithy/property-provider"; -jest.mock("fs"); -jest.mock("./fromWebToken"); +vi.mock("fs"); +vi.mock("./fromWebToken"); const MOCK_CREDS = { accessKeyId: "accessKeyId", @@ -25,13 +26,13 @@ const mockRoleSessionName = "mockRoleSessionName"; describe(fromTokenFile.name, () => { beforeEach(() => { - (readFileSync as jest.Mock).mockReturnValue(mockTokenValue); - (fromWebToken as jest.Mock).mockReturnValue(() => Promise.resolve(MOCK_CREDS)); + vi.mocked(readFileSync).mockReturnValue(mockTokenValue); + vi.mocked(fromWebToken).mockReturnValue(() => Promise.resolve(MOCK_CREDS)); }); afterEach(() => { - jest.clearAllMocks(); - jest.restoreAllMocks(); + vi.clearAllMocks(); + vi.restoreAllMocks(); }); describe("reads config from env", () => { @@ -52,13 +53,13 @@ describe(fromTokenFile.name, () => { }); it(`passes values to ${fromWebToken.name}`, async () => { - const roleAssumerWithWebIdentity = jest.fn(); + const roleAssumerWithWebIdentity = vi.fn(); const creds = await fromTokenFile({ roleAssumerWithWebIdentity, })(); expect(creds).toEqual(MOCK_CREDS); - expect(fromWebToken as jest.Mock).toHaveBeenCalledTimes(1); - const webTokenInit = (fromWebToken as jest.Mock).mock.calls[0][0]; + expect(vi.mocked(fromWebToken)).toHaveBeenCalledTimes(1); + const webTokenInit = vi.mocked(fromWebToken).mock.calls[0][0]; expect(webTokenInit.webIdentityToken).toBe(mockTokenValue); expect(webTokenInit.roleSessionName).toBe(mockRoleSessionName); expect(webTokenInit.roleArn).toBe(mockRoleArn); @@ -66,7 +67,7 @@ describe(fromTokenFile.name, () => { }); it("prefers init parameters over environmental variables", async () => { - const roleAssumerWithWebIdentity = jest.fn(); + const roleAssumerWithWebIdentity = vi.fn(); const init = { webIdentityTokenFile: "anotherTokenFile", roleArn: "anotherRoleArn", @@ -75,22 +76,22 @@ describe(fromTokenFile.name, () => { }; const creds = await fromTokenFile(init)(); expect(creds).toEqual(MOCK_CREDS); - expect(fromWebToken as jest.Mock).toHaveBeenCalledTimes(1); - const webTokenInit = (fromWebToken as jest.Mock).mock.calls[0][0]; + expect(vi.mocked(fromWebToken)).toHaveBeenCalledTimes(1); + const webTokenInit = vi.mocked(fromWebToken).mock.calls[0][0]; expect(webTokenInit.roleSessionName).toBe(init.roleSessionName); expect(webTokenInit.roleArn).toBe(init.roleArn); expect(webTokenInit.roleAssumerWithWebIdentity).toBe(roleAssumerWithWebIdentity); - expect(readFileSync as jest.Mock).toHaveBeenCalledTimes(1); - expect((readFileSync as jest.Mock).mock.calls[0][0]).toBe(init.webIdentityTokenFile); + expect(vi.mocked(readFileSync)).toHaveBeenCalledTimes(1); + expect(vi.mocked(readFileSync).mock.calls[0][0]).toBe(init.webIdentityTokenFile); }); it("throws if ENV_TOKEN_FILE read from disk failed", async () => { const readFileSyncError = new Error("readFileSyncError"); - (readFileSync as jest.Mock).mockImplementation(() => { + vi.mocked(readFileSync).mockImplementation(() => { throw readFileSyncError; }); try { - await fromTokenFile({ roleAssumerWithWebIdentity: jest.fn() })(); + await fromTokenFile({ roleAssumerWithWebIdentity: vi.fn() })(); fail(`Expected error to be thrown`); } catch (error) { expect(error).toEqual(readFileSyncError); @@ -100,7 +101,7 @@ describe(fromTokenFile.name, () => { it("throws if web_identity_token_file read from disk failed", async () => { const readFileSyncError = new Error("readFileSyncError"); - (readFileSync as jest.Mock).mockImplementation(() => { + vi.mocked(readFileSync).mockImplementation(() => { throw readFileSyncError; }); try { @@ -108,7 +109,7 @@ describe(fromTokenFile.name, () => { webIdentityTokenFile: mockTokenFile, roleArn: mockRoleArn, roleSessionName: mockRoleSessionName, - roleAssumerWithWebIdentity: jest.fn(), + roleAssumerWithWebIdentity: vi.fn(), })(); fail(`Expected error to be thrown`); } catch (error) { diff --git a/packages/credential-provider-web-identity/src/fromWebToken.spec.ts b/packages/credential-provider-web-identity/src/fromWebToken.spec.ts index 5afb5df0116c..afb15e5ca4f1 100644 --- a/packages/credential-provider-web-identity/src/fromWebToken.spec.ts +++ b/packages/credential-provider-web-identity/src/fromWebToken.spec.ts @@ -1,4 +1,5 @@ import { getDefaultRoleAssumerWithWebIdentity } from "@aws-sdk/client-sts"; +import { afterEach, describe, expect, test as it, vi } from "vitest"; import { fromWebToken } from "./fromWebToken"; @@ -15,14 +16,14 @@ const MOCK_CREDS = { sessionToken: "sessionToken", }; -jest.mock("@aws-sdk/client-sts", () => ({ - getDefaultRoleAssumerWithWebIdentity: jest.fn().mockReturnValue(() => {}), +vi.mock("@aws-sdk/client-sts", () => ({ + getDefaultRoleAssumerWithWebIdentity: vi.fn().mockReturnValue(() => {}), })); describe("fromWebToken", () => { afterEach(() => { - jest.clearAllMocks(); - jest.restoreAllMocks(); + vi.clearAllMocks(); + vi.restoreAllMocks(); }); it("dynamically imports roleAssumerWithWebIdentity if not provided", async () => { await fromWebToken({ @@ -64,7 +65,7 @@ describe("fromWebToken", () => { it("generates a random value for RoleSessionName if not available", async () => { const mockDateNow = Date.now(); - const spyDateNow = jest.spyOn(Date, "now").mockReturnValueOnce(mockDateNow); + const spyDateNow = vi.spyOn(Date, "now").mockReturnValueOnce(mockDateNow); const creds = await fromWebToken({ webIdentityToken: mockToken, diff --git a/packages/credential-provider-web-identity/vitest.config.ts b/packages/credential-provider-web-identity/vitest.config.ts new file mode 100644 index 000000000000..f360902f18aa --- /dev/null +++ b/packages/credential-provider-web-identity/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e,browser}.spec.{ts,js}"], + include: ["**/*.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/packages/credential-providers/jest.config.js b/packages/credential-providers/jest.config.js deleted file mode 100644 index a8d1c2e49912..000000000000 --- a/packages/credential-providers/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, -}; diff --git a/packages/credential-providers/package.json b/packages/credential-providers/package.json index 71157fa75b25..60191c7b645b 100644 --- a/packages/credential-providers/package.json +++ b/packages/credential-providers/package.json @@ -16,8 +16,9 @@ "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", "extract:docs": "api-extractor run --local", - "test": "jest", - "test:integration": "jest -c jest.config.integ.js" + "test": "vitest run", + "test:integration": "jest -c jest.config.integ.js", + "test:watch": "vitest watch" }, "keywords": [ "aws", diff --git a/packages/credential-providers/src/createCredentialChain.spec.ts b/packages/credential-providers/src/createCredentialChain.spec.ts index 911f41e7b44c..08679a87b3ef 100644 --- a/packages/credential-providers/src/createCredentialChain.spec.ts +++ b/packages/credential-providers/src/createCredentialChain.spec.ts @@ -1,5 +1,6 @@ import { ProviderError } from "@smithy/property-provider"; import { AwsCredentialIdentity, AwsCredentialIdentityProvider } from "@smithy/types"; +import { describe, expect, test as it } from "vitest"; import { createCredentialChain } from "./createCredentialChain"; diff --git a/packages/credential-providers/src/fromCognitoIdentity.spec.ts b/packages/credential-providers/src/fromCognitoIdentity.spec.ts index c520b7fdedb2..a6fabc300bfb 100644 --- a/packages/credential-providers/src/fromCognitoIdentity.spec.ts +++ b/packages/credential-providers/src/fromCognitoIdentity.spec.ts @@ -1,22 +1,23 @@ import { fromCognitoIdentity as coreProvider } from "@aws-sdk/credential-provider-cognito-identity"; +import { beforeEach, describe, expect, test as it, vi } from "vitest"; import { fromCognitoIdentity } from "./fromCognitoIdentity"; -jest.mock("@aws-sdk/client-cognito-identity", () => ({ - CognitoIdentityClient: jest.fn().mockImplementation(function () { +vi.mock("@aws-sdk/client-cognito-identity", () => ({ + CognitoIdentityClient: vi.fn().mockImplementation(function () { return "COGNITO_IDENTITY_CLIENT"; }), })); -jest.mock("@aws-sdk/credential-provider-cognito-identity", () => ({ - fromCognitoIdentity: jest.fn(), +vi.mock("@aws-sdk/credential-provider-cognito-identity", () => ({ + fromCognitoIdentity: vi.fn(), })); describe("fromCognitoIdentity", () => { const identityId = "IDENTITY_ID"; beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("defers to @aws-sdk/credential-provider-cognito-identity", () => { diff --git a/packages/credential-providers/src/fromCognitoIdentityPool.spec.ts b/packages/credential-providers/src/fromCognitoIdentityPool.spec.ts index 1950cb5cf052..63eda01ce85f 100644 --- a/packages/credential-providers/src/fromCognitoIdentityPool.spec.ts +++ b/packages/credential-providers/src/fromCognitoIdentityPool.spec.ts @@ -1,22 +1,23 @@ import { fromCognitoIdentityPool as coreProvider } from "@aws-sdk/credential-provider-cognito-identity"; +import { beforeEach, describe, expect, test as it, vi } from "vitest"; import { fromCognitoIdentityPool } from "./fromCognitoIdentityPool"; -jest.mock("@aws-sdk/client-cognito-identity", () => ({ - CognitoIdentityClient: jest.fn().mockImplementation(function () { +vi.mock("@aws-sdk/client-cognito-identity", () => ({ + CognitoIdentityClient: vi.fn().mockImplementation(function () { return "COGNITO_IDENTITY_CLIENT"; }), })); -jest.mock("@aws-sdk/credential-provider-cognito-identity", () => ({ - fromCognitoIdentityPool: jest.fn(), +vi.mock("@aws-sdk/credential-provider-cognito-identity", () => ({ + fromCognitoIdentityPool: vi.fn(), })); describe("fromCognitoIdentityPool", () => { const identityPoolId = "IDENTITY_POOL_ID"; beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("defers to @aws-sdk/credential-provider-cognito-identity", () => { diff --git a/packages/credential-providers/src/fromIni.spec.ts b/packages/credential-providers/src/fromIni.spec.ts index f14303dbfe75..fb28e0d3e133 100644 --- a/packages/credential-providers/src/fromIni.spec.ts +++ b/packages/credential-providers/src/fromIni.spec.ts @@ -1,23 +1,24 @@ import { getDefaultRoleAssumer, getDefaultRoleAssumerWithWebIdentity } from "@aws-sdk/client-sts"; import { fromIni as coreProvider } from "@aws-sdk/credential-provider-ini"; +import { beforeEach, describe, expect, test as it, vi } from "vitest"; import { fromIni } from "./fromIni"; -const mockRoleAssumer = jest.fn().mockResolvedValue("ROLE_ASSUMER"); -const mockRoleAssumerWithWebIdentity = jest.fn().mockResolvedValue("ROLE_ASSUMER_WITH_WEB_IDENTITY"); +const mockRoleAssumer = vi.fn().mockResolvedValue("ROLE_ASSUMER"); +const mockRoleAssumerWithWebIdentity = vi.fn().mockResolvedValue("ROLE_ASSUMER_WITH_WEB_IDENTITY"); -jest.mock("@aws-sdk/client-sts", () => ({ - getDefaultRoleAssumer: jest.fn().mockImplementation(() => mockRoleAssumer), - getDefaultRoleAssumerWithWebIdentity: jest.fn().mockImplementation(() => mockRoleAssumerWithWebIdentity), +vi.mock("@aws-sdk/client-sts", () => ({ + getDefaultRoleAssumer: vi.fn().mockImplementation(() => mockRoleAssumer), + getDefaultRoleAssumerWithWebIdentity: vi.fn().mockImplementation(() => mockRoleAssumerWithWebIdentity), })); -jest.mock("@aws-sdk/credential-provider-ini", () => ({ - fromIni: jest.fn(), +vi.mock("@aws-sdk/credential-provider-ini", () => ({ + fromIni: vi.fn(), })); describe("fromIni", () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("should not inject default role assumers", () => { @@ -33,8 +34,8 @@ describe("fromIni", () => { it("should use supplied role assumers", () => { const profile = "profile"; - const roleAssumer = jest.fn(); - const roleAssumerWithWebIdentity = jest.fn(); + const roleAssumer = vi.fn(); + const roleAssumerWithWebIdentity = vi.fn(); fromIni({ profile, roleAssumer, roleAssumerWithWebIdentity }); expect(coreProvider).toHaveBeenCalledWith({ profile, diff --git a/packages/credential-providers/src/fromNodeProviderChain.spec.ts b/packages/credential-providers/src/fromNodeProviderChain.spec.ts index 5dcc6d67a4bf..0d2e2f4ec83d 100644 --- a/packages/credential-providers/src/fromNodeProviderChain.spec.ts +++ b/packages/credential-providers/src/fromNodeProviderChain.spec.ts @@ -1,23 +1,24 @@ import { getDefaultRoleAssumer, getDefaultRoleAssumerWithWebIdentity } from "@aws-sdk/client-sts"; import { defaultProvider } from "@aws-sdk/credential-provider-node"; +import { beforeEach, describe, expect, test as it, vi } from "vitest"; import { fromNodeProviderChain } from "./fromNodeProviderChain"; -const mockRoleAssumer = jest.fn().mockResolvedValue("ROLE_ASSUMER"); -const mockRoleAssumerWithWebIdentity = jest.fn().mockResolvedValue("ROLE_ASSUMER_WITH_WEB_IDENTITY"); +const mockRoleAssumer = vi.fn().mockResolvedValue("ROLE_ASSUMER"); +const mockRoleAssumerWithWebIdentity = vi.fn().mockResolvedValue("ROLE_ASSUMER_WITH_WEB_IDENTITY"); -jest.mock("@aws-sdk/client-sts", () => ({ - getDefaultRoleAssumer: jest.fn().mockImplementation(() => mockRoleAssumer), - getDefaultRoleAssumerWithWebIdentity: jest.fn().mockImplementation(() => mockRoleAssumerWithWebIdentity), +vi.mock("@aws-sdk/client-sts", () => ({ + getDefaultRoleAssumer: vi.fn().mockImplementation(() => mockRoleAssumer), + getDefaultRoleAssumerWithWebIdentity: vi.fn().mockImplementation(() => mockRoleAssumerWithWebIdentity), })); -jest.mock("@aws-sdk/credential-provider-node", () => ({ - defaultProvider: jest.fn(), +vi.mock("@aws-sdk/credential-provider-node", () => ({ + defaultProvider: vi.fn(), })); describe(fromNodeProviderChain.name, () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("should not inject default role assumers", () => { @@ -32,8 +33,8 @@ describe(fromNodeProviderChain.name, () => { it("should use supplied role assumers", () => { const profile = "profile"; - const roleAssumer = jest.fn(); - const roleAssumerWithWebIdentity = jest.fn(); + const roleAssumer = vi.fn(); + const roleAssumerWithWebIdentity = vi.fn(); fromNodeProviderChain({ profile, roleAssumer, roleAssumerWithWebIdentity }); expect(defaultProvider).toHaveBeenCalledWith({ profile, diff --git a/packages/credential-providers/src/fromSSO.integ.spec.ts b/packages/credential-providers/src/fromSSO.integ.spec.ts index 9cedc6345df3..902da454ac8c 100644 --- a/packages/credential-providers/src/fromSSO.integ.spec.ts +++ b/packages/credential-providers/src/fromSSO.integ.spec.ts @@ -1,4 +1,3 @@ -import { ListBucketsCommand, S3 } from "@aws-sdk/client-s3"; import fs from "fs"; import { homedir } from "os"; import { join } from "path"; @@ -32,20 +31,13 @@ describe("fromSSO integration test", () => { it("should expand relative homedir", async () => { const mockReadFile = (fs.promises.readFile as jest.Mock).mockResolvedValue(SAMPLE_CONFIG); - const client = new S3({ - region: "eu-west-1", - credentials: fromSSO({ + try { + await fromSSO({ profile: "dev", filepath: "~/custom/path/to/credentials", configFilepath: "~/custom/path/to/config", - }), - }); - - try { - await client.send(new ListBucketsCommand({})); - } catch (e) { - // do nothing - } + })(); + } catch (ignored) {} expect(mockReadFile).toHaveBeenCalledWith(join(homedir(), "custom/path/to/credentials"), "utf8"); expect(mockReadFile).toHaveBeenCalledWith(join(homedir(), "custom/path/to/config"), "utf8"); diff --git a/packages/credential-providers/src/fromSSO.spec.ts b/packages/credential-providers/src/fromSSO.spec.ts index df2bbd427128..ca6b50988d7a 100644 --- a/packages/credential-providers/src/fromSSO.spec.ts +++ b/packages/credential-providers/src/fromSSO.spec.ts @@ -1,5 +1,7 @@ -jest.mock("@aws-sdk/credential-provider-sso", () => ({ - fromSSO: jest.fn(), +import { beforeEach, describe, expect, test as it, vi } from "vitest"; + +vi.mock("@aws-sdk/credential-provider-sso", () => ({ + fromSSO: vi.fn(), })); import { fromSSO as OG } from "@aws-sdk/credential-provider-sso"; @@ -8,11 +10,11 @@ import { fromSSO } from "./fromSSO"; describe("fromSSO", () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("defers to credential-provider-sso", async () => { fromSSO(); - expect(OG as jest.Mock).toHaveBeenCalled(); + expect(vi.mocked(OG)).toHaveBeenCalled(); }); }); diff --git a/packages/credential-providers/src/fromTemporaryCredentials.spec.ts b/packages/credential-providers/src/fromTemporaryCredentials.spec.ts index bed6d6b4baa5..18baddd69b97 100644 --- a/packages/credential-providers/src/fromTemporaryCredentials.spec.ts +++ b/packages/credential-providers/src/fromTemporaryCredentials.spec.ts @@ -1,20 +1,21 @@ import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts"; +import { beforeEach, describe, expect, test as it, vi } from "vitest"; import { fromTemporaryCredentials } from "./fromTemporaryCredentials"; -const mockSend = jest.fn(); -const mockUsePlugin = jest.fn(); -jest.mock("@aws-sdk/client-sts", () => ({ - STSClient: jest.fn().mockImplementation((config) => ({ +const mockSend = vi.fn(); +const mockUsePlugin = vi.fn(); +vi.mock("@aws-sdk/client-sts", () => ({ + STSClient: vi.fn().mockImplementation((config) => ({ config, - send: jest.fn().mockImplementation(async function (command) { + send: vi.fn().mockImplementation(async function (command) { // Mock resolving client credentials provider at send() if (typeof config.credentials === "function") config.credentials = await config.credentials(); return await mockSend(command); }), middlewareStack: { use: mockUsePlugin }, })), - AssumeRoleCommand: jest.fn().mockImplementation(function (params) { + AssumeRoleCommand: vi.fn().mockImplementation(function (params) { // Return the input so we can assert the input parameters in client's send() return { input: params, @@ -33,7 +34,7 @@ describe("fromTemporaryCredentials", () => { const region = "US_BAR_1"; beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); mockSend.mockResolvedValueOnce({ Credentials: { AccessKeyId: "ACCESS_KEY_ID", @@ -61,17 +62,17 @@ describe("fromTemporaryCredentials", () => { secretAccessKey: "SECRET_ACCESS_KEY", sessionToken: "SESSION_TOKEN", }); - expect(STSClient as jest.Mock).toHaveBeenCalledWith({ + expect(vi.mocked(STSClient)).toHaveBeenCalledWith({ credentials: masterCredentials, region, }); expect(mockUsePlugin).toHaveBeenCalledTimes(1); expect(mockUsePlugin).toHaveBeenNthCalledWith(1, plugin); - expect(AssumeRoleCommand as unknown as jest.Mock).toHaveBeenCalledWith({ + expect(AssumeRoleCommand as unknown as any).toHaveBeenCalledWith({ RoleArn, RoleSessionName, }); - expect(mockSend as jest.Mock).toHaveBeenCalledWith({ command: "ASSUME_ROLE", input: options.params }); + expect(vi.mocked(mockSend)).toHaveBeenCalledWith({ command: "ASSUME_ROLE", input: options.params }); }); it("should create STS client if not supplied", async () => { @@ -85,7 +86,7 @@ describe("fromTemporaryCredentials", () => { clientPlugins: [plugin], }); await provider(); - expect(STSClient as jest.Mock).toHaveBeenCalledWith({ + expect(vi.mocked(STSClient)).toHaveBeenCalledWith({ credentials: masterCredentials, }); expect(mockUsePlugin).toHaveBeenCalledTimes(1); @@ -100,7 +101,7 @@ describe("fromTemporaryCredentials", () => { }, }); await provider(); - expect(STSClient as jest.Mock).toHaveBeenCalledWith({}); + expect(vi.mocked(STSClient)).toHaveBeenCalledWith({}); }); it("should create a role session name if none provided", async () => { @@ -108,7 +109,7 @@ describe("fromTemporaryCredentials", () => { params: { RoleArn }, }); await provider(); - expect(AssumeRoleCommand as unknown as jest.Mock).toHaveBeenCalledWith({ + expect(AssumeRoleCommand as unknown as any).toHaveBeenCalledWith({ RoleArn, RoleSessionName: expect.stringMatching(/^aws-sdk-js-/), }); @@ -135,29 +136,29 @@ describe("fromTemporaryCredentials", () => { })); const credentials = await provider(); expect(mockSend.mock.calls.length).toBe(3); - expect((AssumeRoleCommand as unknown as jest.Mock).mock.calls.length).toBe(3); + expect((AssumeRoleCommand as unknown as any).mock.calls.length).toBe(3); expect(credentials.accessKeyId).toBe("access_id_from_third"); // Creates STS Client with right master credentials and assume role with // expected role arn. - expect((STSClient as jest.Mock).mock.results.length).toBe(3); - const outmostClient = (STSClient as jest.Mock).mock.results[0].value; + expect(vi.mocked(STSClient).mock.results.length).toBe(3); + const outmostClient = vi.mocked(STSClient).mock.results[0].value; expect(outmostClient.config.credentials).toEqual(expect.objectContaining({ accessKeyId: "access_id_from_second" })); - expect((outmostClient.send as jest.Mock).mock.calls.length).toBe(1); - expect((outmostClient.send as jest.Mock).mock.calls[0][0].input).toEqual( + expect((outmostClient.send as any).mock.calls.length).toBe(1); + expect((outmostClient.send as any).mock.calls[0][0].input).toEqual( expect.objectContaining({ RoleArn: roleArnOf("third") }) ); - const middleClient = (STSClient as jest.Mock).mock.results[1].value; + const middleClient = vi.mocked(STSClient).mock.results[1].value; expect(middleClient.config.credentials).toEqual(expect.objectContaining({ accessKeyId: "access_id_from_first" })); - expect((middleClient.send as jest.Mock).mock.calls.length).toBe(1); - expect((middleClient.send as jest.Mock).mock.calls[0][0].input).toEqual( + expect((middleClient.send as any).mock.calls.length).toBe(1); + expect((middleClient.send as any).mock.calls[0][0].input).toEqual( expect.objectContaining({ RoleArn: roleArnOf("second") }) ); - const innermostClient = (STSClient as jest.Mock).mock.results[2].value; + const innermostClient = vi.mocked(STSClient).mock.results[2].value; expect(innermostClient.config.credentials).toEqual(undefined); - expect((innermostClient.send as jest.Mock).mock.calls.length).toBe(1); - expect((innermostClient.send as jest.Mock).mock.calls[0][0].input).toEqual( + expect((innermostClient.send as any).mock.calls.length).toBe(1); + expect((innermostClient.send as any).mock.calls[0][0].input).toEqual( expect.objectContaining({ RoleArn: roleArnOf("first") }) ); @@ -168,13 +169,13 @@ describe("fromTemporaryCredentials", () => { // Should not create extra clients if credentials is still valid await provider(); - expect((STSClient as jest.Mock).mock.results.length).toBe(3); + expect(vi.mocked(STSClient).mock.results.length).toBe(3); }); it("should support assuming a role with multi-factor authentication", async () => { const SerialNumber = "SERIAL_NUMBER"; const mfaCode = "MFA_CODE"; - const mfaCodeProvider = jest.fn().mockResolvedValue(mfaCode); + const mfaCodeProvider = vi.fn().mockResolvedValue(mfaCode); const provider = fromTemporaryCredentials({ params: { RoleArn, SerialNumber, RoleSessionName }, mfaCodeProvider, diff --git a/packages/credential-providers/src/fromTokenFile.spec.ts b/packages/credential-providers/src/fromTokenFile.spec.ts index f92bffedb4ac..42c791bd373e 100644 --- a/packages/credential-providers/src/fromTokenFile.spec.ts +++ b/packages/credential-providers/src/fromTokenFile.spec.ts @@ -1,21 +1,22 @@ import { getDefaultRoleAssumerWithWebIdentity } from "@aws-sdk/client-sts"; import { fromTokenFile as coreProvider } from "@aws-sdk/credential-provider-web-identity"; +import { beforeEach, describe, expect, test as it, vi } from "vitest"; import { fromTokenFile } from "./fromTokenFile"; -const mockRoleAssumerWithWebIdentity = jest.fn().mockResolvedValue("ROLE_ASSUMER_WITH_WEB_IDENTITY"); +const mockRoleAssumerWithWebIdentity = vi.fn().mockResolvedValue("ROLE_ASSUMER_WITH_WEB_IDENTITY"); -jest.mock("@aws-sdk/client-sts", () => ({ - getDefaultRoleAssumerWithWebIdentity: jest.fn().mockImplementation(() => mockRoleAssumerWithWebIdentity), +vi.mock("@aws-sdk/client-sts", () => ({ + getDefaultRoleAssumerWithWebIdentity: vi.fn().mockImplementation(() => mockRoleAssumerWithWebIdentity), })); -jest.mock("@aws-sdk/credential-provider-web-identity", () => ({ - fromTokenFile: jest.fn(), +vi.mock("@aws-sdk/credential-provider-web-identity", () => ({ + fromTokenFile: vi.fn(), })); describe("fromTokenFile", () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("should not inject default role assumer", () => { diff --git a/packages/credential-providers/src/fromWebToken.spec.ts b/packages/credential-providers/src/fromWebToken.spec.ts index 3ca8a0ad1d5a..7abb5c48eb2c 100644 --- a/packages/credential-providers/src/fromWebToken.spec.ts +++ b/packages/credential-providers/src/fromWebToken.spec.ts @@ -1,16 +1,17 @@ import { getDefaultRoleAssumerWithWebIdentity } from "@aws-sdk/client-sts"; import { fromWebToken as coreProvider } from "@aws-sdk/credential-provider-web-identity"; +import { beforeEach, describe, expect, test as it, vi } from "vitest"; import { fromWebToken } from "./fromWebToken"; -const mockRoleAssumerWithWebIdentity = jest.fn().mockResolvedValue("ROLE_ASSUMER_WITH_WEB_IDENTITY"); +const mockRoleAssumerWithWebIdentity = vi.fn().mockResolvedValue("ROLE_ASSUMER_WITH_WEB_IDENTITY"); -jest.mock("@aws-sdk/client-sts", () => ({ - getDefaultRoleAssumerWithWebIdentity: jest.fn().mockImplementation(() => mockRoleAssumerWithWebIdentity), +vi.mock("@aws-sdk/client-sts", () => ({ + getDefaultRoleAssumerWithWebIdentity: vi.fn().mockImplementation(() => mockRoleAssumerWithWebIdentity), })); -jest.mock("@aws-sdk/credential-provider-web-identity", () => ({ - fromWebToken: jest.fn(), +vi.mock("@aws-sdk/credential-provider-web-identity", () => ({ + fromWebToken: vi.fn(), })); describe("fromWebToken", () => { @@ -18,7 +19,7 @@ describe("fromWebToken", () => { const webIdentityToken = "WEB_IDENTITY_TOKEN"; beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("should not inject default role assumer", () => { diff --git a/packages/credential-providers/vitest.config.integ.ts b/packages/credential-providers/vitest.config.integ.ts new file mode 100644 index 000000000000..35e2b17c4f3d --- /dev/null +++ b/packages/credential-providers/vitest.config.integ.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["**/*.integ.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/packages/credential-providers/vitest.config.ts b/packages/credential-providers/vitest.config.ts new file mode 100644 index 000000000000..f360902f18aa --- /dev/null +++ b/packages/credential-providers/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e,browser}.spec.{ts,js}"], + include: ["**/*.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/packages/ec2-metadata-service/jest.config.e2e.js b/packages/ec2-metadata-service/jest.config.e2e.js deleted file mode 100644 index c3aa6055ef75..000000000000 --- a/packages/ec2-metadata-service/jest.config.e2e.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - preset: "ts-jest", - testMatch: ["**/*.e2e.spec.ts"], - bail: true, -}; diff --git a/packages/ec2-metadata-service/jest.config.js b/packages/ec2-metadata-service/jest.config.js deleted file mode 100644 index a8d1c2e49912..000000000000 --- a/packages/ec2-metadata-service/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, -}; diff --git a/packages/ec2-metadata-service/package.json b/packages/ec2-metadata-service/package.json index 6917efb894b9..ef6fed568f00 100644 --- a/packages/ec2-metadata-service/package.json +++ b/packages/ec2-metadata-service/package.json @@ -9,8 +9,10 @@ "build:types": "tsc -p tsconfig.types.json", "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", - "test": "jest --passWithNoTests", - "test:e2e": "jest -c jest.config.e2e.js" + "test": "vitest run --passWithNoTests", + "test:e2e": "vitest run -c vitest.config.e2e.ts", + "test:watch": "vitest watch --passWithNoTests", + "test:e2e:watch": "vitest watch -c vitest.config.e2e.ts" }, "author": { "name": "AWS SDK for JavaScript Team", diff --git a/packages/ec2-metadata-service/src/MetadataService.e2e.spec.ts b/packages/ec2-metadata-service/src/MetadataService.e2e.spec.ts index a9dfefbd3bfd..66f3f43ed70f 100644 --- a/packages/ec2-metadata-service/src/MetadataService.e2e.spec.ts +++ b/packages/ec2-metadata-service/src/MetadataService.e2e.spec.ts @@ -1,4 +1,5 @@ import { fromInstanceMetadata } from "@aws-sdk/credential-providers"; +import { beforeAll, describe, expect, test as it, vi } from "vitest"; import { MetadataService } from "./MetadataService"; @@ -63,7 +64,7 @@ describe("MetadataService E2E Tests", () => { if (!metadataServiceAvailable) { return; } - jest.spyOn(metadataService, "fetchMetadataToken").mockImplementation(async () => { + vi.spyOn(metadataService, "fetchMetadataToken").mockImplementation(async () => { throw { name: "TimeoutError" }; // Simulating TimeoutError }); // Attempt to fetch metadata, expecting IMDSv1 fallback (request without token) @@ -82,7 +83,7 @@ describe("MetadataService E2E Tests", () => { } const httpErrors = [403, 404, 405]; for (const errorCode of httpErrors) { - jest.spyOn(metadataService, "fetchMetadataToken").mockImplementationOnce(async () => { + vi.spyOn(metadataService, "fetchMetadataToken").mockImplementationOnce(async () => { throw { statusCode: errorCode }; }); const metadata = (await metadataService.request("/latest/meta-data/", {})) as string; diff --git a/packages/ec2-metadata-service/vitest.config.e2e.ts b/packages/ec2-metadata-service/vitest.config.e2e.ts new file mode 100644 index 000000000000..8e3e8ccc5726 --- /dev/null +++ b/packages/ec2-metadata-service/vitest.config.e2e.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["**/*.e2e.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/packages/ec2-metadata-service/vitest.config.ts b/packages/ec2-metadata-service/vitest.config.ts new file mode 100644 index 000000000000..f360902f18aa --- /dev/null +++ b/packages/ec2-metadata-service/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e,browser}.spec.{ts,js}"], + include: ["**/*.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/packages/endpoint-cache/jest.config.js b/packages/endpoint-cache/jest.config.js deleted file mode 100644 index a8d1c2e49912..000000000000 --- a/packages/endpoint-cache/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, -}; diff --git a/packages/endpoint-cache/package.json b/packages/endpoint-cache/package.json index febce51e63f8..e957082abb4e 100644 --- a/packages/endpoint-cache/package.json +++ b/packages/endpoint-cache/package.json @@ -9,7 +9,8 @@ "build:types": "tsc -p tsconfig.types.json", "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", - "test": "jest --passWithNoTests" + "test": "vitest run", + "test:watch": "vitest watch" }, "author": { "name": "AWS SDK for JavaScript Team", diff --git a/packages/endpoint-cache/src/EndpointCache.spec.ts b/packages/endpoint-cache/src/EndpointCache.spec.ts index e1e146015211..fc8f51b52691 100644 --- a/packages/endpoint-cache/src/EndpointCache.spec.ts +++ b/packages/endpoint-cache/src/EndpointCache.spec.ts @@ -1,9 +1,10 @@ import LRUCache from "mnemonist/lru-cache"; +import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest"; import { Endpoint } from "./Endpoint"; import { EndpointCache } from "./EndpointCache"; -jest.mock("mnemonist/lru-cache"); +vi.mock("mnemonist/lru-cache"); describe(EndpointCache.name, () => { let endpointCache: EndpointCache; @@ -11,11 +12,11 @@ describe(EndpointCache.name, () => { const key = "key"; const now = Date.now(); - const set = jest.fn(); - const get = jest.fn(); - const peek = jest.fn(); - const has = jest.fn(); - const clear = jest.fn(); + const set = vi.fn(); + const get = vi.fn(); + const peek = vi.fn(); + const has = vi.fn(); + const clear = vi.fn(); const mockEndpoints = [ { Address: "addressA", CachePeriodInMinutes: 1 }, @@ -32,7 +33,7 @@ describe(EndpointCache.name, () => { Math.max(...endpoints.map((endpoint) => endpoint.CachePeriodInMinutes)); beforeEach(() => { - (LRUCache as unknown as jest.Mock).mockReturnValueOnce({ + (LRUCache as unknown as any).mockReturnValueOnce({ set, get, peek, @@ -43,7 +44,7 @@ describe(EndpointCache.name, () => { }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("passes capacity to LRUCache", () => { @@ -57,7 +58,7 @@ describe(EndpointCache.name, () => { const endpointsWithExpiry = getEndpointsWithExpiry(mockEndpoints); peek.mockReturnValue(endpointsWithExpiry); get.mockReturnValue(endpointsWithExpiry); - jest.spyOn(Date, "now").mockImplementation(() => now); + vi.spyOn(Date, "now").mockImplementation(() => now); }); const verifyHasAndGetCalls = () => { @@ -96,7 +97,7 @@ describe(EndpointCache.name, () => { it("returns undefined if endpoints have expired", () => { const maxCachePeriod = getMaxCachePeriodInMins(mockEndpoints); - jest.spyOn(Date, "now").mockImplementation(() => now + (maxCachePeriod + 1) * 60 * 1000); + vi.spyOn(Date, "now").mockImplementation(() => now + (maxCachePeriod + 1) * 60 * 1000); expect(endpointCache.get(key)).toBeUndefined(); verifyHasAndGetCalls(); expect(set).toHaveBeenCalledTimes(1); @@ -111,7 +112,7 @@ describe(EndpointCache.name, () => { }); it("returns un-expired endpoint", () => { - jest.spyOn(Date, "now").mockImplementation(() => now + 90 * 1000); + vi.spyOn(Date, "now").mockImplementation(() => now + 90 * 1000); expect(endpointCache.getEndpoint(key)).toEqual(mockEndpoints[1].Address); verifyHasAndGetCalls(); expect(set).not.toHaveBeenCalled(); @@ -119,7 +120,7 @@ describe(EndpointCache.name, () => { [0, 1].forEach((index) => { it(`returns un-expired endpoint at index ${index}`, () => { - jest.spyOn(Math, "floor").mockImplementation(() => index); + vi.spyOn(Math, "floor").mockImplementation(() => index); expect(mockEndpoints.map((endpoint) => endpoint.Address)).toContain(endpointCache.getEndpoint(key)); verifyHasAndGetCalls(); expect(set).not.toHaveBeenCalled(); @@ -130,7 +131,7 @@ describe(EndpointCache.name, () => { describe("set", () => { beforeEach(() => { - jest.spyOn(Date, "now").mockImplementation(() => now); + vi.spyOn(Date, "now").mockImplementation(() => now); }); it("converts CachePeriodInMinutes to Expires before caching", () => { diff --git a/packages/endpoint-cache/vitest.config.ts b/packages/endpoint-cache/vitest.config.ts new file mode 100644 index 000000000000..f360902f18aa --- /dev/null +++ b/packages/endpoint-cache/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e,browser}.spec.{ts,js}"], + include: ["**/*.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/packages/eventstream-handler-node/jest.config.js b/packages/eventstream-handler-node/jest.config.js deleted file mode 100644 index a8d1c2e49912..000000000000 --- a/packages/eventstream-handler-node/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -const base = require("../../jest.config.base.js"); - -module.exports = { - ...base, -}; diff --git a/packages/eventstream-handler-node/package.json b/packages/eventstream-handler-node/package.json index eeaa59f14e70..390b006e7895 100644 --- a/packages/eventstream-handler-node/package.json +++ b/packages/eventstream-handler-node/package.json @@ -9,7 +9,8 @@ "build:types": "tsc -p tsconfig.types.json", "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", - "test": "jest" + "test": "vitest run", + "test:watch": "vitest watch" }, "main": "./dist-cjs/index.js", "module": "./dist-es/index.js", diff --git a/packages/eventstream-handler-node/src/EventSigningStream.spec.ts b/packages/eventstream-handler-node/src/EventSigningStream.spec.ts index 5d6da99e0279..7f10966fdbea 100644 --- a/packages/eventstream-handler-node/src/EventSigningStream.spec.ts +++ b/packages/eventstream-handler-node/src/EventSigningStream.spec.ts @@ -1,6 +1,7 @@ import { EventStreamCodec } from "@smithy/eventstream-codec"; import { Message, MessageHeaders, SignedMessage } from "@smithy/types"; import { fromUtf8, toUtf8 } from "@smithy/util-utf8"; +import { afterEach, describe, expect, test as it, vi } from "vitest"; import { EventSigningStream } from "./EventSigningStream"; @@ -11,7 +12,7 @@ describe("EventSigningStream", () => { Date = originalDate; }); - it("should sign an eventstream payload properly", (done) => { + it("should sign an eventstream payload properly", async () => { const eventStreamCodec = new EventStreamCodec(toUtf8, fromUtf8); const message1: Message = { headers: {}, @@ -40,7 +41,7 @@ describe("EventSigningStream", () => { }, }, ]; - const mockMessageSigner = jest + const mockMessageSigner = vi .fn() .mockReturnValueOnce({ message: message1, signature: "7369676e617475726531" } as SignedMessage) //'signature1' .mockReturnValueOnce({ message: message2, signature: "7369676e617475726532" } as SignedMessage); //'signature2' @@ -65,6 +66,10 @@ describe("EventSigningStream", () => { signingStream.on("data", (chunk) => { output.push(eventStreamCodec.decode(chunk).headers); }); + + let resolve: (value?: unknown) => void; + const done = new Promise((r) => (resolve = r)); + signingStream.on("end", () => { expect(output).toEqual(expected); expect(mockMessageSigner.mock.calls[0][0].priorSignature).toBe("initial"); @@ -75,7 +80,7 @@ describe("EventSigningStream", () => { expect(mockMessageSigner.mock.calls[1][1].signingDate.getTime()).toBe( (expected[1][":date"].value as Date).getTime() ); - done(); + resolve(); }); signingStream.on("error", (err) => { throw err; @@ -84,5 +89,7 @@ describe("EventSigningStream", () => { signingStream.write(input); } signingStream.end(); + + await done; }); }); diff --git a/packages/eventstream-handler-node/src/EventStreamPayloadHandler.spec.ts b/packages/eventstream-handler-node/src/EventStreamPayloadHandler.spec.ts index a2b8f25d0edd..e3ffa9a2592a 100644 --- a/packages/eventstream-handler-node/src/EventStreamPayloadHandler.spec.ts +++ b/packages/eventstream-handler-node/src/EventStreamPayloadHandler.spec.ts @@ -1,12 +1,13 @@ import { EventStreamCodec } from "@smithy/eventstream-codec"; import { Decoder, Encoder, FinalizeHandler, FinalizeHandlerArguments, HttpRequest, MessageSigner } from "@smithy/types"; import { PassThrough, Readable } from "stream"; +import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest"; import { EventSigningStream } from "./EventSigningStream"; import { EventStreamPayloadHandler } from "./EventStreamPayloadHandler"; -jest.mock("./EventSigningStream"); -jest.mock("@smithy/eventstream-codec"); +vi.mock("./EventSigningStream"); +vi.mock("@smithy/eventstream-codec"); describe(EventStreamPayloadHandler.name, () => { const collectData = (stream: Readable) => { @@ -19,20 +20,20 @@ describe(EventStreamPayloadHandler.name, () => { }; const mockMessageSigner: MessageSigner = { - sign: jest.fn(), - signMessage: jest.fn(), + sign: vi.fn(), + signMessage: vi.fn(), }; - const mockUtf8Decoder: Decoder = jest.fn(); - const mockUtf8encoder: Encoder = jest.fn(); - const mockNextHandler: FinalizeHandler = jest.fn(); + const mockUtf8Decoder: Decoder = vi.fn(); + const mockUtf8encoder: Encoder = vi.fn(); + const mockNextHandler: FinalizeHandler = vi.fn(); beforeEach(() => { - (EventSigningStream as unknown as jest.Mock).mockImplementation(() => new PassThrough()); - (EventStreamCodec as jest.Mock).mockImplementation(() => {}); + (EventSigningStream as unknown as any).mockImplementation(() => new PassThrough()); + vi.mocked(EventStreamCodec).mockImplementation(() => {}); }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it("should throw if request payload is not a stream", () => { diff --git a/packages/eventstream-handler-node/vitest.config.ts b/packages/eventstream-handler-node/vitest.config.ts new file mode 100644 index 000000000000..f360902f18aa --- /dev/null +++ b/packages/eventstream-handler-node/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e,browser}.spec.{ts,js}"], + include: ["**/*.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/scripts/validation/vitest-validation.js b/scripts/validation/vitest-validation.js index d7c7d0e6f55d..4ceaa246038f 100644 --- a/scripts/validation/vitest-validation.js +++ b/scripts/validation/vitest-validation.js @@ -2,7 +2,27 @@ const fs = require("fs"); const path = require("path"); const walk = require("../utils/walk"); -const paths = [path.join(__dirname, "..", "..", "packages", "core")]; +const paths = [ + path.join(__dirname, "..", "..", "lib", "lib-dynamodb"), + path.join(__dirname, "..", "..", "lib", "lib-storage"), + path.join(__dirname, "..", "..", "packages", "body-checksum-browser"), + path.join(__dirname, "..", "..", "packages", "body-checksum-node"), + path.join(__dirname, "..", "..", "packages", "chunked-stream-reader-node"), + path.join(__dirname, "..", "..", "packages", "cloudfront-signer"), + path.join(__dirname, "..", "..", "packages", "core"), + path.join(__dirname, "..", "..", "packages", "credential-provider-cognito-identity"), + path.join(__dirname, "..", "..", "packages", "credential-provider-env"), + path.join(__dirname, "..", "..", "packages", "credential-provider-http"), + path.join(__dirname, "..", "..", "packages", "credential-provider-ini"), + path.join(__dirname, "..", "..", "packages", "credential-provider-node"), + path.join(__dirname, "..", "..", "packages", "credential-provider-process"), + path.join(__dirname, "..", "..", "packages", "credential-provider-sso"), + path.join(__dirname, "..", "..", "packages", "credential-provider-web-identity"), + path.join(__dirname, "..", "..", "packages", "credential-providers"), + path.join(__dirname, "..", "..", "packages", "ec2-metadata-service"), + path.join(__dirname, "..", "..", "packages", "endpoint-cache"), + path.join(__dirname, "..", "..", "packages", "eventstream-handler-node"), +]; (async () => { for (const folder of paths) { @@ -13,6 +33,7 @@ const paths = [path.join(__dirname, "..", "..", "packages", "core")]; console.log("setting unit test to vitest"); pkgJson.scripts.test = "vitest run"; + pkgJson.scripts["test:watch"] = "vitest watch"; fs.rmSync(path.join(folder, "jest.config.js")); fs.writeFileSync( path.join(folder, "vitest.config.ts"), @@ -27,12 +48,15 @@ const paths = [path.join(__dirname, "..", "..", "packages", "core")]; }); ` ); + } else if (pkgJson.scripts.test.includes("vitest")) { + pkgJson.scripts["test:watch"] ??= "vitest watch --passWithNot"; } } - for (const testType of ["integ", "e2e"]) { + for (const testType of [/* "integ", */ "e2e"]) { const script = testType === "integ" ? "integration" : testType; if (pkgJson.scripts[`test:${script}`]) { + pkgJson.scripts[`test:${script}:watch`] = `vitest watch -c vitest.config.${testType}.ts`; if (pkgJson.scripts[`test:${script}`].includes("jest")) { console.log(`setting ${testType} test to vitest`); @@ -75,7 +99,11 @@ const paths = [path.join(__dirname, "..", "..", "packages", "core")]; const imports = ["test as it"]; - contents = contents.replace(/\((\w+) as (jest|vi).Mock\)/g, "vi.mocked($1)"); + contents = contents + .replace(/\((\w+) as (jest|vi).Mock\)/g, "(vi.mocked($1))") + .replace(/(jest|vi)\.requireActual\((.*?);\)/g, "await vi.importActual($1) as any;") + .replace(/ as (vi|jest)\.Mock/g, " as any") + .replace(/xit/g, "it.skip"); for (const [old, _new] of Object.entries(namespaces)) { if (contents.includes(old + ".") || contents.includes(_new + ".")) { diff --git a/tests/e2e/get-integ-test-resources.js b/tests/e2e/get-integ-test-resources.js index 17075028e7cc..46f66476bfd3 100644 --- a/tests/e2e/get-integ-test-resources.js +++ b/tests/e2e/get-integ-test-resources.js @@ -6,10 +6,8 @@ const { S3ControlClient, ListMultiRegionAccessPointsCommand } = require("../../c const { ensureTestStack } = require("./ensure-test-stack"); const { deleteStaleChangesets } = require("./delete-stale-changesets"); -const logger = { ...console, debug() {} }; - exports.getIntegTestResources = async () => { - const cloudformation = new CloudFormationClient({ logger }); + const cloudformation = new CloudFormationClient({}); const region = await cloudformation.config.region(); const stackName = "SdkReleaseV3IntegTestResourcesStack"; @@ -23,8 +21,6 @@ exports.getIntegTestResources = async () => { new DescribeStackResourcesCommand({ StackName: stackName }) ); - console.log(`${stackName} Stack Resources: `, stackResources); - const identityPoolId = stackResources.filter((resource) => resource.ResourceType === "AWS::Cognito::IdentityPool")[0] .PhysicalResourceId; @@ -36,9 +32,9 @@ exports.getIntegTestResources = async () => { (resource) => resource.ResourceType === "AWS::S3::MultiRegionAccessPoint" )[0].PhysicalResourceId; - const sts = new STSClient({ logger }); + const sts = new STSClient({}); const { Account: AccountId } = await sts.send(new GetCallerIdentityCommand({})); - const s3Control = new S3ControlClient({ logger, region: "us-west-2" }); + const s3Control = new S3ControlClient({ region: "us-west-2" }); const { AccessPoints } = await s3Control.send(new ListMultiRegionAccessPointsCommand({ AccountId })); const { Alias } = AccessPoints.find((accesspoint) => accesspoint.Name === multiRegionAccessPointName); const mrapArn = `arn:aws:s3::${AccountId}:accesspoint/${Alias}`; diff --git a/turbo.json b/turbo.json index 7147c0561fd0..67e299cd6222 100644 --- a/turbo.json +++ b/turbo.json @@ -40,7 +40,10 @@ "inputs": [] }, "test": { - "dependsOn": ["^build:cjs"] + "cache": false + }, + "test:integration": { + "cache": false } } } diff --git a/vite.workspace.ts b/vite.workspace.ts deleted file mode 100644 index 2765d526194a..000000000000 --- a/vite.workspace.ts +++ /dev/null @@ -1 +0,0 @@ -export default ["{packages,clients,lib,private}/*/vitest.config.{e2e,integ,browser}.ts"]; diff --git a/vitest.config.e2e.ts b/vitest.config.e2e.ts new file mode 100644 index 000000000000..63cc1b795d99 --- /dev/null +++ b/vitest.config.e2e.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["lib/lib-dynamodb/**/*.e2e.spec.{ts,js}", "lib/lib-storage/**/*.e2e.spec.{ts,js}"], + environment: "node", + }, +}); diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 000000000000..e1db99b6dcbf --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,29 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + exclude: ["**/*.{integ,e2e,browser}.spec.{ts,js}"], + include: [ + "lib/lib-dynamodb/**/*.spec.{ts,js}", + "lib/lib-storage/**/*.spec.{ts,js}", + // "packages/body-checksum-browser/**/*.spec.{ts,js}", + "packages/body-checksum-node/**/*.spec.{ts,js}", + "packages/chunked-stream-reader-node/**/*.spec.{ts,js}", + "packages/cloudfront-signer/**/*.spec.{ts,js}", + "packages/core/**/*.spec.{ts,js}", + "packages/credential-provider-cognito-identity/**/*.spec.{ts,js}", + "packages/credential-provider-env/**/*.spec.{ts,js}", + "packages/credential-provider-http/**/*.spec.{ts,js}", + "packages/credential-provider-ini/**/*.spec.{ts,js}", + "packages/credential-provider-node/**/*.spec.{ts,js}", + "packages/credential-provider-process/**/*.spec.{ts,js}", + "packages/credential-provider-sso/**/*.spec.{ts,js}", + "packages/credential-provider-web-identity/**/*.spec.{ts,js}", + "packages/credential-providers/**/*.spec.{ts,js}", + "packages/ec2-metadata-service/**/*.spec.{ts,js}", + "packages/endpoint-cache/**/*.spec.{ts,js}", + "packages/eventstream-handler-node/**/*.spec.{ts,js}", + ], + environment: "node", + }, +}); diff --git a/yarn.lock b/yarn.lock index 377869c85869..a9d45230dcf1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6259,7 +6259,7 @@ entities@2.2.0: resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== -entities@^4.4.0: +entities@^4.4.0, entities@^4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== @@ -7500,6 +7500,24 @@ handlebars@4.7.7, handlebars@^4.7.7: optionalDependencies: uglify-js "^3.1.4" +happy-dom@14.12.3: + version "14.12.3" + resolved "https://registry.yarnpkg.com/happy-dom/-/happy-dom-14.12.3.tgz#1b5892c670461fd1db041bee690981c22d3d521f" + integrity sha512-vsYlEs3E9gLwA1Hp+w3qzu+RUDFf4VTT8cyKqVICoZ2k7WM++Qyd2LwzyTi5bqMJFiIC/vNpTDYuxdreENRK/g== + dependencies: + entities "^4.5.0" + webidl-conversions "^7.0.0" + whatwg-mimetype "^3.0.0" + +happy-dom@^15.7.4: + version "15.7.4" + resolved "https://registry.yarnpkg.com/happy-dom/-/happy-dom-15.7.4.tgz#05aade59c1d307336001b7004c76dfc6a829f220" + integrity sha512-r1vadDYGMtsHAAsqhDuk4IpPvr6N8MGKy5ntBo7tSdim+pWDxus2PNqOcOt8LuDZ4t3KJHE+gCuzupcx/GKnyQ== + dependencies: + entities "^4.5.0" + webidl-conversions "^7.0.0" + whatwg-mimetype "^3.0.0" + har-schema@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz"