diff --git a/packages/util-stream/jest.config.e2e.js b/packages/util-stream/jest.config.e2e.js deleted file mode 100644 index b4d9bee23f48..000000000000 --- a/packages/util-stream/jest.config.e2e.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - preset: "ts-jest", - testMatch: ["**/*.e2e.spec.ts"], -}; diff --git a/packages/util-stream/package.json b/packages/util-stream/package.json index 34666acfa935..6421d563b765 100644 --- a/packages/util-stream/package.json +++ b/packages/util-stream/package.json @@ -10,8 +10,7 @@ "build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4", "clean": "rimraf ./dist-* && rimraf *.tsbuildinfo", "test": "jest && karma start karma.conf.js", - "test:integration": "jest -c jest.config.integ.js", - "test:e2e": "jest -c jest.config.e2e.js" + "test:integration": "jest -c jest.config.integ.js" }, "main": "./dist-cjs/index.js", "module": "./dist-es/index.js", diff --git a/packages/util-stream/src/util-stream.integ.spec.ts b/packages/util-stream/src/util-stream.integ.spec.ts index dc19ee134813..7b1622aa94a0 100644 --- a/packages/util-stream/src/util-stream.integ.spec.ts +++ b/packages/util-stream/src/util-stream.integ.spec.ts @@ -1,4 +1,9 @@ import { Lambda } from "@aws-sdk/client-lambda"; +import { HttpResponse } from "@aws-sdk/protocol-http"; +import { HttpRequest as IHttpRequest } from "@aws-sdk/types"; +import { Uint8ArrayBlobAdapter } from "@aws-sdk/util-stream"; +import { fromUtf8 } from "@aws-sdk/util-utf8"; +import { Readable } from "stream"; import { requireRequestsFrom } from "../../../private/aws-util-test/src"; @@ -41,4 +46,74 @@ describe("util-stream", () => { }); }); }); + + describe("blob helper integration", () => { + const FunctionName = "echo"; + + const lambda = new Lambda({ + region: "us-west-2", + }); + + requireRequestsFrom(lambda).toMatch({ + method: "POST", + hostname: "lambda.us-west-2.amazonaws.com", + query: {}, + headers: { + "content-type": "application/octet-stream", + host: "lambda.us-west-2.amazonaws.com", + }, + protocol: "https:", + path: "/2015-03-31/functions/echo/invocations", + }); + + lambda.config.requestHandler = new (class { + async handle(request: IHttpRequest) { + return { + response: new HttpResponse({ + statusCode: 200, + body: typeof request.body === "string" ? fromUtf8(request.body) : Uint8Array.from(request.body), + }), + }; + } + })(); + + it("should allow string as payload blob and allow conversion of output payload blob to string", async () => { + const payload = JSON.stringify({ hello: "world" }); + const invoke = await lambda.invoke({ FunctionName, Payload: payload }); + expect(JSON.parse(invoke?.Payload?.transformToString() ?? "{}")).toEqual({ hello: "world" }); + }); + + it("should allow Uint8Array as payload blob", async () => { + const payload = Uint8ArrayBlobAdapter.fromString(JSON.stringify({ hello: "world" })); + const invoke = await lambda.invoke({ FunctionName, Payload: payload }); + expect(JSON.parse(invoke?.Payload?.transformToString() ?? "{}")).toEqual({ hello: "world" }); + }); + + it("should allow buffer as payload blob", async () => { + // note: Buffer extends Uint8Array + const payload = Buffer.from(Uint8ArrayBlobAdapter.fromString(JSON.stringify({ hello: "world" }))); + const invoke = await lambda.invoke({ FunctionName, Payload: payload }); + expect(JSON.parse(invoke?.Payload?.transformToString() ?? "{}")).toEqual({ hello: "world" }); + }); + + it("should allow stream as payload blob but not be able to sign it", async () => { + const payload = Readable.from(Buffer.from(Uint8ArrayBlobAdapter.fromString(JSON.stringify({ hello: "world" }))), { + encoding: "utf-8", + }); + expect(JSON.parse(await streamToString(payload))).toEqual({ hello: "world" }); + await lambda.invoke({ FunctionName, Payload: payload }).catch((e) => { + expect(e.toString()).toContain("InvalidSignatureException"); + }); + expect.hasAssertions(); + }); + }); + + function streamToString(stream: Readable): Promise { + const chunks: any[] = []; + return new Promise((resolve, reject) => { + stream.on("data", (chunk) => chunks.push(Buffer.from(chunk))); + stream.on("error", (err) => reject(err)); + stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8"))); + }); + } }); diff --git a/packages/util-stream/test/function.zip b/packages/util-stream/test/function.zip deleted file mode 100644 index bc37c3b33225..000000000000 Binary files a/packages/util-stream/test/function.zip and /dev/null differ diff --git a/packages/util-stream/test/index.mjs b/packages/util-stream/test/index.mjs deleted file mode 100644 index e1b98f02447f..000000000000 --- a/packages/util-stream/test/index.mjs +++ /dev/null @@ -1,6 +0,0 @@ -/** - * function.zip contains this file. - */ -export const handler = async (event) => { - return event; -}; diff --git a/packages/util-stream/test/setup.ts b/packages/util-stream/test/setup.ts deleted file mode 100644 index 5e4816dfb4b4..000000000000 --- a/packages/util-stream/test/setup.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { AttachedPolicy, CreateRoleResponse, GetRoleResponse, IAM } from "@aws-sdk/client-iam"; -import { - GetFunctionConfigurationCommandOutput, - Lambda, - Runtime, - waitUntilFunctionActiveV2, - waitUntilFunctionUpdated, -} from "@aws-sdk/client-lambda"; -import fs from "fs"; -export const FunctionName = "aws-sdk-js-v3-e2e-echo"; -export const Handler = "index.handler"; -const LAMBDA_ROLE_NAME = "aws-sdk-js-v3-e2e-LambdaRole"; - -export async function setup() { - const lambda = new Lambda({ - region: "us-west-2", - }); - const getFn: null | GetFunctionConfigurationCommandOutput = await lambda - .getFunctionConfiguration({ - FunctionName, - }) - .catch(() => null); - - if (getFn) { - return; - } - - const iam = new IAM({ - region: "us-west-2", - }); - - const roleName = LAMBDA_ROLE_NAME; - const role: null | GetRoleResponse | CreateRoleResponse = await iam - .getRole({ - RoleName: roleName, - }) - .catch(() => null); - - if (!role) { - console.info("Creating role", roleName); - await iam.createRole({ - RoleName: roleName, - Path: "/", - Description: "aws sdk js v3 lambda test role", - AssumeRolePolicyDocument: JSON.stringify({ - Version: "2012-10-17", - Statement: [ - { - Effect: "Allow", - Action: ["sts:AssumeRole"], - Principal: { - Service: ["lambda.amazonaws.com"], - }, - }, - ], - }), - }); - } else { - console.info("Role exists", roleName); - } - - const listAttachedRolePolicies = await iam.listAttachedRolePolicies({ - RoleName: roleName, - }); - const policies = listAttachedRolePolicies.AttachedPolicies || []; - - const existingPolicies = policies.reduce((acc: Record, cur: AttachedPolicy) => { - if (cur.PolicyName) { - acc[cur.PolicyName] = true; - } - return acc; - }, {} as Record); - - const required = ["AWSLambda_FullAccess"]; - - for (const requiredPolicy of required) { - if (!existingPolicies[requiredPolicy]) { - console.info("Attaching policy to role", requiredPolicy, roleName); - await iam.attachRolePolicy({ - RoleName: roleName, - PolicyArn: `arn:aws:iam::aws:policy/${requiredPolicy}`, - }); - } else { - console.info("Policy exists on role", requiredPolicy, roleName); - } - } - - const getRole: null | GetRoleResponse = await iam - .getRole({ - RoleName: roleName, - }) - .catch(() => null); - if (!getRole) { - throw new Error("Role not found."); - } else { - console.info("Role found", roleName); - } - - const roleArn = getRole.Role!.Arn!; - - if (getFn) { - console.info("Function exists:", FunctionName); - - if ((getFn.Timeout ?? 0) < 5 * 60 || getFn?.Handler !== Handler) { - await lambda.updateFunctionConfiguration({ - FunctionName, - Handler, - Timeout: 5 * 60, - }); - await waitUntilFunctionUpdated( - { - client: lambda, - maxWaitTime: 40, - }, - { - FunctionName, - } - ); - } - // await lambda.updateFunctionCode({ - // FunctionName, - // ZipFile: fs.readFileSync(require.resolve("./function.zip")), - // }); - // console.info("Function code/configuration updated:", FunctionName); - } else { - await lambda.createFunction({ - FunctionName, - Role: roleArn, - Code: { - ZipFile: fs.readFileSync(require.resolve("./function.zip")), - }, - Runtime: Runtime.nodejs16x, - Description: `aws sdk js v3 e2e test echo`, - Timeout: 300, - Handler, - }); - console.info("Function created:", FunctionName); - } - - await waitUntilFunctionActiveV2( - { - maxWaitTime: 40, - client: lambda, - }, - { - FunctionName, - } - ); -} diff --git a/packages/util-stream/test/util-stream-blob.e2e.spec.ts b/packages/util-stream/test/util-stream-blob.e2e.spec.ts deleted file mode 100644 index fc89a3373e07..000000000000 --- a/packages/util-stream/test/util-stream-blob.e2e.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Lambda } from "@aws-sdk/client-lambda"; -import { Uint8ArrayBlobAdapter } from "@aws-sdk/util-stream"; -import { Readable } from "stream"; - -import { FunctionName, setup } from "./setup"; - -xdescribe("blob e2e", () => { - jest.setTimeout(100000); - - const lambda = new Lambda({ - region: "us-west-2", - }); - - beforeAll(async () => { - await setup(); - }); - - it("should allow string as payload blob and allow conversion of output payload blob to string", async () => { - const payload = JSON.stringify({ hello: "world" }); - const invoke = await lambda.invoke({ FunctionName, Payload: payload }); - expect(JSON.parse(invoke?.Payload?.transformToString() ?? "{}")).toEqual({ hello: "world" }); - }); - - it("should allow Uint8Array as payload blob", async () => { - const payload = Uint8ArrayBlobAdapter.fromString(JSON.stringify({ hello: "world" })); - const invoke = await lambda.invoke({ FunctionName, Payload: payload }); - expect(JSON.parse(invoke?.Payload?.transformToString() ?? "{}")).toEqual({ hello: "world" }); - }); - - it("should allow buffer as payload blob", async () => { - // note: Buffer extends Uint8Array - const payload = Buffer.from(Uint8ArrayBlobAdapter.fromString(JSON.stringify({ hello: "world" }))); - const invoke = await lambda.invoke({ FunctionName, Payload: payload }); - expect(JSON.parse(invoke?.Payload?.transformToString() ?? "{}")).toEqual({ hello: "world" }); - }); - - it("should allow stream as payload blob but not be able to sign it", async () => { - const payload = Readable.from(Buffer.from(Uint8ArrayBlobAdapter.fromString(JSON.stringify({ hello: "world" }))), { - encoding: "utf-8", - }); - expect(JSON.parse(await streamToString(payload))).toEqual({ hello: "world" }); - await lambda.invoke({ FunctionName, Payload: payload }).catch((e) => { - expect(e.toString()).toContain("InvalidSignatureException"); - }); - expect.hasAssertions(); - }); -}); - -function streamToString(stream: Readable): Promise { - const chunks: any[] = []; - return new Promise((resolve, reject) => { - stream.on("data", (chunk) => chunks.push(Buffer.from(chunk))); - stream.on("error", (err) => reject(err)); - stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8"))); - }); -}