From e44f262a9e257c2f67b8a8793a0937aadb58c790 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Fri, 29 Apr 2022 18:12:19 +0000 Subject: [PATCH 01/10] feat(integ-tests): enhancements to integ-tests This PR contains various enhancements including - API ergonomics improvements - renamed `queryAws` to `awsApiCall` - Now using `Match` from @aws-cdk/assertions for the assertions provider - `DeployAssert` now creates its own stack - Additional assertion types (OBJECT_LIKE) - utility for invoking lambda functions (separate from `awsApiCall`) --- .../aws-lambda-destinations/package.json | 1 + ...TestCaseDeployAssert175C057C.template.json | 245 +++++++ .../index.js | 643 ++++++++++++++++++ .../aws-cdk-lambda-destinations.template.json | 22 +- .../test/destinations.integ.snapshot/cdk.out | 2 +- .../destinations.integ.snapshot/integ.json | 9 +- .../destinations.integ.snapshot/manifest.json | 102 ++- .../destinations.integ.snapshot/tree.json | 302 +++++++- .../test/integ.destinations.ts | 50 +- packages/@aws-cdk/aws-lambda/package.json | 1 + ...TestCaseDeployAssert4F885E41.template.json | 211 ++++++ .../cdk-integ-lambda-bundling.template.json | 30 +- .../test/bundling.integ.snapshot/cdk.out | 2 +- .../test/bundling.integ.snapshot/integ.json | 10 +- .../bundling.integ.snapshot/manifest.json | 112 ++- .../test/bundling.integ.snapshot/tree.json | 264 ++++++- .../aws-lambda/test/integ.bundling.ts | 26 +- .../aws-stepfunctions-tasks/package.json | 1 + .../test/eventbridge/integ.put-events.ts | 21 +- ...TestCaseDeployAssert4F885E41.template.json | 199 ++++++ .../index.js | 643 ++++++++++++++++++ ...eventbridge-put-events-integ.template.json | 17 +- .../put-events.integ.snapshot/cdk.out | 2 +- .../put-events.integ.snapshot/integ.json | 9 +- .../put-events.integ.snapshot/manifest.json | 92 ++- .../put-events.integ.snapshot/tree.json | 290 +++++++- .../lib/integ-tests/test-case.ts | 7 + .../lib/runner/integ-test-runner.ts | 73 +- .../integ-runner/lib/workers/common.ts | 40 ++ .../lib/workers/extract/extract_worker.ts | 26 +- .../test/runner/integ-test-runner.test.ts | 4 + packages/@aws-cdk/integ-tests/README.md | 262 ++++++- .../integ-tests/lib/assertions/assertions.ts | 41 +- .../integ-tests/lib/assertions/common.ts | 58 ++ .../lib/assertions/deploy-assert.ts | 108 +-- .../integ-tests/lib/assertions/index.ts | 1 + .../providers/lambda-handler/assertion.ts | 67 +- .../providers/lambda-handler/sdk.ts | 14 +- .../providers/lambda-handler/types.ts | 14 +- .../providers/lambda-handler/utils.ts | 13 + .../lib/assertions/providers/provider.ts | 205 +++++- .../integ-tests/lib/assertions/sdk.ts | 192 +++++- packages/@aws-cdk/integ-tests/lib/index.ts | 1 + .../@aws-cdk/integ-tests/lib/test-case.ts | 118 +++- packages/@aws-cdk/integ-tests/package.json | 27 +- .../integ-tests/rosetta/default.ts-fixture | 16 +- .../test/assertions/assertions.test.ts | 48 -- .../test/assertions/deploy-assert.test.ts | 146 ++-- .../lambda-handler/assertion.test.ts | 83 ++- .../providers/lambda-handler/results.test.ts | 59 -- .../assertions/providers/provider.test.ts | 118 +++- .../integ-tests/test/assertions/sdk.test.ts | 247 ++++++- .../test/manifest-synthesizer.test.ts | 79 ++- 53 files changed, 4857 insertions(+), 516 deletions(-) create mode 100644 packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/IntegDefaultTestCaseDeployAssert175C057C.template.json create mode 100644 packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle/index.js create mode 100644 packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/IntegTestDefaultTestCaseDeployAssert4F885E41.template.json create mode 100644 packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/IntegTestDefaultTestCaseDeployAssert4F885E41.template.json create mode 100644 packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle/index.js create mode 100644 packages/@aws-cdk/integ-tests/lib/assertions/common.ts create mode 100644 packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/utils.ts delete mode 100644 packages/@aws-cdk/integ-tests/test/assertions/assertions.test.ts delete mode 100644 packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/results.test.ts diff --git a/packages/@aws-cdk/aws-lambda-destinations/package.json b/packages/@aws-cdk/aws-lambda-destinations/package.json index de7196e7e46bb..cf40af003ccfb 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/package.json +++ b/packages/@aws-cdk/aws-lambda-destinations/package.json @@ -72,6 +72,7 @@ "license": "Apache-2.0", "devDependencies": { "@aws-cdk/assertions": "0.0.0", + "@aws-cdk/integ-tests": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/integ-runner": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/IntegDefaultTestCaseDeployAssert175C057C.template.json b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/IntegDefaultTestCaseDeployAssert175C057C.template.json new file mode 100644 index 0000000000000..b0af5c7e09ddf --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/IntegDefaultTestCaseDeployAssert175C057C.template.json @@ -0,0 +1,245 @@ +{ + "Resources": { + "LambdaInvoked12df417a1b74909abb3ea643735a310": { + "Type": "Custom::DeployAssert@SdkCallLambdainvoke", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F", + "Arn" + ] + }, + "service": "Lambda", + "api": "invoke", + "parameters": { + "FunctionName": { + "Fn::ImportValue": "aws-cdk-lambda-destinations:ExportsOutputRefSnsSqsC4810B27404A5AFF" + }, + "InvocationType": "Event", + "Payload": "{\"status\":\"OK\"}" + }, + "flattenResponse": "false", + "salt": "1651499763724" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "LambdaInvoked12df417a1b74909abb3ea643735a310InvokeF590C289": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::ImportValue": "aws-cdk-lambda-destinations:ExportsOutputRefSnsSqsC4810B27404A5AFF" + }, + "Principal": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73", + "Arn" + ] + } + } + }, + "SdkQuerySQSreceiveMessage": { + "Type": "Custom::DeployAssert@SdkCallSQSreceiveMessage", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F", + "Arn" + ] + }, + "service": "SQS", + "api": "receiveMessage", + "parameters": { + "QueueUrl": { + "Fn::ImportValue": "aws-cdk-lambda-destinations:ExportsOutputRefQueue4A7E3555425E8BD3" + }, + "WaitTimeSeconds": 20 + }, + "flattenResponse": "true", + "salt": "1651499763725" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "ReceiveMessage": { + "Type": "Custom::DeployAssert@AssertEquals", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F", + "Arn" + ] + }, + "actual": { + "Fn::GetAtt": [ + "SdkQuerySQSreceiveMessage", + "apiCallResponse.Messages.0.Body" + ] + }, + "expected": "{\"requestContext\":{\"condition\":\"Success\"},\"requestPayload\":{\"status\":\"OK\"},\"responseContext\":{\"statusCode\":200},\"responsePayload\":\"success\"}", + "assertionType": "objectLike", + "salt": "1651499763725" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "lambda:Invoke" + ], + "Effect": "Allow", + "Resource": [ + "*" + ] + }, + { + "Action": [ + "lambda:InvokeFunction" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":lambda:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":function:", + { + "Fn::ImportValue": "aws-cdk-lambda-destinations:ExportsOutputRefSnsSqsC4810B27404A5AFF" + } + ] + ] + } + ] + }, + { + "Action": [ + "sqs:ReceiveMessage" + ], + "Effect": "Allow", + "Resource": [ + "*" + ] + } + ] + } + } + ] + } + }, + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Runtime": "nodejs14.x", + "Code": { + "S3Bucket": { + "Ref": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4" + } + ] + } + ] + } + ] + ] + } + }, + "Timeout": 120, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73", + "Arn" + ] + } + } + } + }, + "Outputs": { + "AssertionResultsReceiveMessage": { + "Value": { + "Fn::GetAtt": [ + "ReceiveMessage", + "data" + ] + } + } + }, + "Parameters": { + "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB": { + "Type": "String", + "Description": "S3 bucket for asset \"37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0\"" + }, + "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4": { + "Type": "String", + "Description": "S3 key for asset version \"37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0\"" + }, + "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0ArtifactHash4C05AFBC": { + "Type": "String", + "Description": "Artifact hash for asset \"37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0\"" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle/index.js b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle/index.js new file mode 100644 index 0000000000000..3a860016dea59 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle/index.js @@ -0,0 +1,643 @@ +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getOwnPropSymbols = Object.getOwnPropertySymbols; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __propIsEnum = Object.prototype.propertyIsEnumerable; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __spreadValues = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + if (__getOwnPropSymbols) + for (var prop of __getOwnPropSymbols(b)) { + if (__propIsEnum.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + } + return a; +}; +var __esm = (fn, res) => function __init() { + return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// ../assertions/lib/matcher.ts +var Matcher, MatchResult; +var init_matcher = __esm({ + "../assertions/lib/matcher.ts"() { + Matcher = class { + static isMatcher(x) { + return x && x instanceof Matcher; + } + }; + MatchResult = class { + constructor(target) { + this.failures = []; + this.captures = /* @__PURE__ */ new Map(); + this.finalized = false; + this.target = target; + } + push(matcher, path, message) { + return this.recordFailure({ matcher, path, message }); + } + recordFailure(failure) { + this.failures.push(failure); + return this; + } + hasFailed() { + return this.failures.length !== 0; + } + get failCount() { + return this.failures.length; + } + compose(id, inner) { + const innerF = inner.failures; + this.failures.push(...innerF.map((f) => { + return { path: [id, ...f.path], message: f.message, matcher: f.matcher }; + })); + inner.captures.forEach((vals, capture) => { + vals.forEach((value) => this.recordCapture({ capture, value })); + }); + return this; + } + finished() { + if (this.finalized) { + return this; + } + if (this.failCount === 0) { + this.captures.forEach((vals, cap) => cap._captured.push(...vals)); + } + this.finalized = true; + return this; + } + toHumanStrings() { + return this.failures.map((r) => { + const loc = r.path.length === 0 ? "" : ` at ${r.path.join("")}`; + return "" + r.message + loc + ` (using ${r.matcher.name} matcher)`; + }); + } + recordCapture(options) { + let values = this.captures.get(options.capture); + if (values === void 0) { + values = []; + } + values.push(options.value); + this.captures.set(options.capture, values); + } + }; + } +}); + +// ../assertions/lib/private/matchers/absent.ts +var AbsentMatch; +var init_absent = __esm({ + "../assertions/lib/private/matchers/absent.ts"() { + init_matcher(); + AbsentMatch = class extends Matcher { + constructor(name) { + super(); + this.name = name; + } + test(actual) { + const result = new MatchResult(actual); + if (actual !== void 0) { + result.recordFailure({ + matcher: this, + path: [], + message: `Received ${actual}, but key should be absent` + }); + } + return result; + } + }; + } +}); + +// ../assertions/lib/private/type.ts +function getType(obj) { + return Array.isArray(obj) ? "array" : typeof obj; +} +var init_type = __esm({ + "../assertions/lib/private/type.ts"() { + } +}); + +// ../assertions/lib/match.ts +var match_exports = {}; +__export(match_exports, { + Match: () => Match +}); +var Match, LiteralMatch, ArrayMatch, ObjectMatch, SerializedJson, NotMatch, AnyMatch, StringLikeRegexpMatch; +var init_match = __esm({ + "../assertions/lib/match.ts"() { + init_matcher(); + init_absent(); + init_type(); + Match = class { + static absent() { + return new AbsentMatch("absent"); + } + static arrayWith(pattern) { + return new ArrayMatch("arrayWith", pattern); + } + static arrayEquals(pattern) { + return new ArrayMatch("arrayEquals", pattern, { subsequence: false }); + } + static exact(pattern) { + return new LiteralMatch("exact", pattern, { partialObjects: false }); + } + static objectLike(pattern) { + return new ObjectMatch("objectLike", pattern); + } + static objectEquals(pattern) { + return new ObjectMatch("objectEquals", pattern, { partial: false }); + } + static not(pattern) { + return new NotMatch("not", pattern); + } + static serializedJson(pattern) { + return new SerializedJson("serializedJson", pattern); + } + static anyValue() { + return new AnyMatch("anyValue"); + } + static stringLikeRegexp(pattern) { + return new StringLikeRegexpMatch("stringLikeRegexp", pattern); + } + }; + LiteralMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.partialObjects = options.partialObjects ?? false; + if (Matcher.isMatcher(this.pattern)) { + throw new Error("LiteralMatch cannot directly contain another matcher. Remove the top-level matcher or nest it more deeply."); + } + } + test(actual) { + if (Array.isArray(this.pattern)) { + return new ArrayMatch(this.name, this.pattern, { subsequence: false, partialObjects: this.partialObjects }).test(actual); + } + if (typeof this.pattern === "object") { + return new ObjectMatch(this.name, this.pattern, { partial: this.partialObjects }).test(actual); + } + const result = new MatchResult(actual); + if (typeof this.pattern !== typeof actual) { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected type ${typeof this.pattern} but received ${getType(actual)}` + }); + return result; + } + if (actual !== this.pattern) { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected ${this.pattern} but received ${actual}` + }); + } + return result; + } + }; + ArrayMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.subsequence = options.subsequence ?? true; + this.partialObjects = options.partialObjects ?? false; + } + test(actual) { + if (!Array.isArray(actual)) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected type array but received ${getType(actual)}` + }); + } + if (!this.subsequence && this.pattern.length !== actual.length) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected array of length ${this.pattern.length} but received ${actual.length}` + }); + } + let patternIdx = 0; + let actualIdx = 0; + const result = new MatchResult(actual); + while (patternIdx < this.pattern.length && actualIdx < actual.length) { + const patternElement = this.pattern[patternIdx]; + const matcher = Matcher.isMatcher(patternElement) ? patternElement : new LiteralMatch(this.name, patternElement, { partialObjects: this.partialObjects }); + const matcherName = matcher.name; + if (this.subsequence && (matcherName == "absent" || matcherName == "anyValue")) { + throw new Error(`The Matcher ${matcherName}() cannot be nested within arrayWith()`); + } + const innerResult = matcher.test(actual[actualIdx]); + if (!this.subsequence || !innerResult.hasFailed()) { + result.compose(`[${actualIdx}]`, innerResult); + patternIdx++; + actualIdx++; + } else { + actualIdx++; + } + } + for (; patternIdx < this.pattern.length; patternIdx++) { + const pattern = this.pattern[patternIdx]; + const element = Matcher.isMatcher(pattern) || typeof pattern === "object" ? " " : ` [${pattern}] `; + result.recordFailure({ + matcher: this, + path: [], + message: `Missing element${element}at pattern index ${patternIdx}` + }); + } + return result; + } + }; + ObjectMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.partial = options.partial ?? true; + } + test(actual) { + if (typeof actual !== "object" || Array.isArray(actual)) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected type object but received ${getType(actual)}` + }); + } + const result = new MatchResult(actual); + if (!this.partial) { + for (const a of Object.keys(actual)) { + if (!(a in this.pattern)) { + result.recordFailure({ + matcher: this, + path: [`/${a}`], + message: "Unexpected key" + }); + } + } + } + for (const [patternKey, patternVal] of Object.entries(this.pattern)) { + if (!(patternKey in actual) && !(patternVal instanceof AbsentMatch)) { + result.recordFailure({ + matcher: this, + path: [`/${patternKey}`], + message: `Missing key '${patternKey}' among {${Object.keys(actual).join(",")}}` + }); + continue; + } + const matcher = Matcher.isMatcher(patternVal) ? patternVal : new LiteralMatch(this.name, patternVal, { partialObjects: this.partial }); + const inner = matcher.test(actual[patternKey]); + result.compose(`/${patternKey}`, inner); + } + return result; + } + }; + SerializedJson = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const result = new MatchResult(actual); + if (getType(actual) !== "string") { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected JSON as a string but found ${getType(actual)}` + }); + return result; + } + let parsed; + try { + parsed = JSON.parse(actual); + } catch (err) { + if (err instanceof SyntaxError) { + result.recordFailure({ + matcher: this, + path: [], + message: `Invalid JSON string: ${actual}` + }); + return result; + } else { + throw err; + } + } + const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); + const innerResult = matcher.test(parsed); + result.compose(`(${this.name})`, innerResult); + return result; + } + }; + NotMatch = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); + const innerResult = matcher.test(actual); + const result = new MatchResult(actual); + if (innerResult.failCount === 0) { + result.recordFailure({ + matcher: this, + path: [], + message: `Found unexpected match: ${JSON.stringify(actual, void 0, 2)}` + }); + } + return result; + } + }; + AnyMatch = class extends Matcher { + constructor(name) { + super(); + this.name = name; + } + test(actual) { + const result = new MatchResult(actual); + if (actual == null) { + result.recordFailure({ + matcher: this, + path: [], + message: "Expected a value but found none" + }); + } + return result; + } + }; + StringLikeRegexpMatch = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const result = new MatchResult(actual); + const regex = new RegExp(this.pattern, "gm"); + if (typeof actual !== "string") { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected a string, but got '${typeof actual}'` + }); + } + if (!regex.test(actual)) { + result.recordFailure({ + matcher: this, + path: [], + message: `String '${actual}' did not match pattern '${this.pattern}'` + }); + } + return result; + } + }; + } +}); + +// lib/assertions/providers/lambda-handler/index.ts +var lambda_handler_exports = {}; +__export(lambda_handler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(lambda_handler_exports); + +// lib/assertions/providers/lambda-handler/base.ts +var https = __toESM(require("https")); +var url = __toESM(require("url")); +var CustomResourceHandler = class { + constructor(event, context) { + this.event = event; + this.context = context; + this.timedOut = false; + this.timeout = setTimeout(async () => { + await this.respond({ + status: "FAILED", + reason: "Lambda Function Timeout", + data: this.context.logStreamName + }); + this.timedOut = true; + }, context.getRemainingTimeInMillis() - 1200); + this.event = event; + this.physicalResourceId = extractPhysicalResourceId(event); + } + async handle() { + try { + console.log(`Event: ${JSON.stringify(this.event)}`); + const response = await this.processEvent(this.event.ResourceProperties); + console.log(`Event output : ${JSON.stringify(response)}`); + await this.respond({ + status: "SUCCESS", + reason: "OK", + data: response + }); + } catch (e) { + console.log(e); + await this.respond({ + status: "FAILED", + reason: e.message ?? "Internal Error" + }); + } finally { + clearTimeout(this.timeout); + } + } + respond(response) { + if (this.timedOut) { + return; + } + const cfResponse = { + Status: response.status, + Reason: response.reason, + PhysicalResourceId: this.physicalResourceId, + StackId: this.event.StackId, + RequestId: this.event.RequestId, + LogicalResourceId: this.event.LogicalResourceId, + NoEcho: false, + Data: response.data + }; + const responseBody = JSON.stringify(cfResponse); + console.log("Responding to CloudFormation", responseBody); + const parsedUrl = url.parse(this.event.ResponseURL); + const requestOptions = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: "PUT", + headers: { "content-type": "", "content-length": responseBody.length } + }; + return new Promise((resolve, reject) => { + try { + const request2 = https.request(requestOptions, resolve); + request2.on("error", reject); + request2.write(responseBody); + request2.end(); + } catch (e) { + reject(e); + } + }); + } +}; +function extractPhysicalResourceId(event) { + switch (event.RequestType) { + case "Create": + return event.LogicalResourceId; + case "Update": + case "Delete": + return event.PhysicalResourceId; + } +} + +// lib/assertions/providers/lambda-handler/assertion.ts +var { Match: Match2 } = (init_match(), __toCommonJS(match_exports)); +var AssertionHandler = class extends CustomResourceHandler { + async processEvent(request2) { + let actual = request2.actual; + const expected = decodeCall(request2.expected); + let result; + let matcher; + console.log(`Testing equality between ${JSON.stringify(request2.actual)} and ${JSON.stringify(request2.expected)}`); + switch (request2.assertionType) { + case "objectLike": + if (typeof actual === "string") { + actual = JSON.parse(actual); + } + matcher = Match2.objectLike(expected); + break; + case "equals": + matcher = Match2.exact(expected); + break; + case "arrayWith": + matcher = Match2.arrayWith(expected); + break; + default: + throw new Error(`Unsupported query type ${request2.assertionType}`); + } + const matchResult = matcher.test(actual); + matchResult.finished(); + if (matchResult.hasFailed()) { + result = { + data: JSON.stringify({ + status: "fail", + message: [ + ...matchResult.toHumanStrings(), + JSON.stringify(matchResult.target, void 0, 2) + ].join("\n") + }) + }; + } else { + result = { + data: JSON.stringify({ + status: "pass" + }) + }; + } + return result; + } +}; +function decodeCall(call) { + if (!call) { + return void 0; + } + return JSON.parse(call); +} + +// lib/assertions/providers/lambda-handler/results.ts +var ResultsCollectionHandler = class extends CustomResourceHandler { + async processEvent(request2) { + const reduced = request2.assertionResults.reduce((agg, result, idx) => { + const msg = result.status === "pass" ? "pass" : `fail - ${result.message}`; + return `${agg} +Test${idx}: ${msg}`; + }, "").trim(); + return { message: reduced }; + } +}; + +// lib/assertions/providers/lambda-handler/utils.ts +function decode(object) { + return JSON.parse(JSON.stringify(object), (_k, v) => { + switch (v) { + case "TRUE:BOOLEAN": + return true; + case "FALSE:BOOLEAN": + return false; + case !isNaN(+v): + return +v; + default: + return v; + } + }); +} + +// lib/assertions/providers/lambda-handler/sdk.ts +function flatten(object) { + return Object.assign({}, ...function _flatten(child, path = []) { + return [].concat(...Object.keys(child).map((key) => { + const childKey = Buffer.isBuffer(child[key]) ? child[key].toString("utf8") : child[key]; + return typeof childKey === "object" && childKey !== null ? _flatten(childKey, path.concat([key])) : { [path.concat([key]).join(".")]: childKey }; + })); + }(object)); +} +var SdkHandler = class extends CustomResourceHandler { + async processEvent(request2) { + const AWS = require("aws-sdk"); + console.log(`AWS SDK VERSION: ${AWS.VERSION}`); + const service = new AWS[request2.service](); + const response = await service[request2.api](request2.parameters && decode(request2.parameters)).promise(); + console.log(`SDK response received ${JSON.stringify(response)}`); + delete response.ResponseMetadata; + const respond = { + apiCallResponse: response + }; + const flatData = __spreadValues({}, flatten(respond)); + return request2.flattenResponse === "true" ? flatData : respond; + } +}; + +// lib/assertions/providers/lambda-handler/types.ts +var ASSERT_RESOURCE_TYPE = "Custom::DeployAssert@AssertEquals"; +var RESULTS_RESOURCE_TYPE = "Custom::DeployAssert@ResultsCollection"; +var SDK_RESOURCE_TYPE_PREFIX = "Custom::DeployAssert@SdkCall"; + +// lib/assertions/providers/lambda-handler/index.ts +async function handler(event, context) { + const provider = createResourceHandler(event, context); + await provider.handle(); +} +function createResourceHandler(event, context) { + if (event.ResourceType.startsWith(SDK_RESOURCE_TYPE_PREFIX)) { + return new SdkHandler(event, context); + } + switch (event.ResourceType) { + case ASSERT_RESOURCE_TYPE: + return new AssertionHandler(event, context); + case RESULTS_RESOURCE_TYPE: + return new ResultsCollectionHandler(event, context); + default: + throw new Error(`Unsupported resource type "${event.ResourceType}`); + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/aws-cdk-lambda-destinations.template.json b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/aws-cdk-lambda-destinations.template.json index 87d704ed4327a..84e4ea60c1c0e 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/aws-cdk-lambda-destinations.template.json +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/aws-cdk-lambda-destinations.template.json @@ -80,7 +80,7 @@ "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "ZipFile": "exports.handler = async (event) => {\n if (event === 'OK') return 'success';\n throw new Error('failure');\n };" + "ZipFile": "exports.handler = async (event) => {\n if (event.status === 'OK') return 'success';\n throw new Error('failure');\n };" }, "Role": { "Fn::GetAtt": [ @@ -281,7 +281,7 @@ "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "ZipFile": "exports.handler = async (event) => {\n if (event === 'OK') return 'success';\n throw new Error('failure');\n };" + "ZipFile": "exports.handler = async (event) => {\n if (event.status === 'OK') return 'success';\n throw new Error('failure');\n };" }, "Role": { "Fn::GetAtt": [ @@ -391,5 +391,23 @@ "MaximumRetryAttempts": 0 } } + }, + "Outputs": { + "ExportsOutputRefSnsSqsC4810B27404A5AFF": { + "Value": { + "Ref": "SnsSqsC4810B27" + }, + "Export": { + "Name": "aws-cdk-lambda-destinations:ExportsOutputRefSnsSqsC4810B27404A5AFF" + } + }, + "ExportsOutputRefQueue4A7E3555425E8BD3": { + "Value": { + "Ref": "Queue4A7E3555" + }, + "Export": { + "Name": "aws-cdk-lambda-destinations:ExportsOutputRefQueue4A7E3555425E8BD3" + } + } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/cdk.out index 90bef2e09ad39..2efc89439fab8 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"17.0.0"} \ No newline at end of file +{"version":"18.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/integ.json b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/integ.json index 20b73bceabb1f..0add177715c8f 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/integ.json +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/integ.json @@ -1,14 +1,11 @@ { "version": "18.0.0", "testCases": { - "aws-lambda-destinations/test/integ.destinations": { + "DefaultTestCase": { "stacks": [ "aws-cdk-lambda-destinations" ], - "diffAssets": false, - "stackUpdateWorkflow": true + "assertionStack": "IntegDefaultTestCaseDeployAssert175C057C" } - }, - "synthContext": {}, - "enableLookups": false + } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/manifest.json index 2684d0546624f..05160d2f6dfdb 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "17.0.0", + "version": "18.0.0", "artifacts": { "Tree": { "type": "cdk:tree", @@ -104,9 +104,109 @@ "type": "aws:cdk:logicalId", "data": "MySpecialAliasEventInvokeConfig05FF4E2F" } + ], + "/aws-cdk-lambda-destinations/Exports/Output{\"Ref\":\"SnsSqsC4810B27\"}": [ + { + "type": "aws:cdk:logicalId", + "data": "ExportsOutputRefSnsSqsC4810B27404A5AFF" + } + ], + "/aws-cdk-lambda-destinations/Exports/Output{\"Ref\":\"Queue4A7E3555\"}": [ + { + "type": "aws:cdk:logicalId", + "data": "ExportsOutputRefQueue4A7E3555425E8BD3" + } ] }, "displayName": "aws-cdk-lambda-destinations" + }, + "IntegDefaultTestCaseDeployAssert175C057C": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "IntegDefaultTestCaseDeployAssert175C057C.template.json", + "validateOnSynth": false + }, + "dependencies": [ + "aws-cdk-lambda-destinations" + ], + "metadata": { + "/Integ/DefaultTestCase/DeployAssert": [ + { + "type": "aws:cdk:asset", + "data": { + "path": "asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle", + "id": "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", + "packaging": "zip", + "sourceHash": "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", + "s3BucketParameter": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB", + "s3KeyParameter": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4", + "artifactHashParameter": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0ArtifactHash4C05AFBC" + } + } + ], + "/Integ/DefaultTestCase/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "LambdaInvoked12df417a1b74909abb3ea643735a310" + } + ], + "/Integ/DefaultTestCase/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/Invoke": [ + { + "type": "aws:cdk:logicalId", + "data": "LambdaInvoked12df417a1b74909abb3ea643735a310InvokeF590C289" + } + ], + "/Integ/DefaultTestCase/DeployAssert/Default/SdkQuerySQSreceiveMessage/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "SdkQuerySQSreceiveMessage" + } + ], + "/Integ/DefaultTestCase/DeployAssert/Default/ReceiveMessage/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "ReceiveMessage" + } + ], + "/Integ/DefaultTestCase/DeployAssert/Default/ReceiveMessage/AssertionResults": [ + { + "type": "aws:cdk:logicalId", + "data": "AssertionResultsReceiveMessage" + } + ], + "/Integ/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73" + } + ], + "/Integ/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F" + } + ], + "/Integ/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3Bucket": [ + { + "type": "aws:cdk:logicalId", + "data": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB" + } + ], + "/Integ/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3VersionKey": [ + { + "type": "aws:cdk:logicalId", + "data": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4" + } + ], + "/Integ/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/ArtifactHash": [ + { + "type": "aws:cdk:logicalId", + "data": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0ArtifactHash4C05AFBC" + } + ] + }, + "displayName": "Integ/DefaultTestCase/DeployAssert" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/tree.json b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/tree.json index 60299ae021368..b85b047bc472a 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/tree.json @@ -175,7 +175,7 @@ "aws:cdk:cloudformation:type": "AWS::Lambda::Function", "aws:cdk:cloudformation:props": { "code": { - "zipFile": "exports.handler = async (event) => {\n if (event === 'OK') return 'success';\n throw new Error('failure');\n };" + "zipFile": "exports.handler = async (event) => {\n if (event.status === 'OK') return 'success';\n throw new Error('failure');\n };" }, "role": { "Fn::GetAtt": [ @@ -495,7 +495,7 @@ "aws:cdk:cloudformation:type": "AWS::Lambda::Function", "aws:cdk:cloudformation:props": { "code": { - "zipFile": "exports.handler = async (event) => {\n if (event === 'OK') return 'success';\n throw new Error('failure');\n };" + "zipFile": "exports.handler = async (event) => {\n if (event.status === 'OK') return 'success';\n throw new Error('failure');\n };" }, "role": { "Fn::GetAtt": [ @@ -681,12 +681,310 @@ "fqn": "@aws-cdk/aws-lambda.Alias", "version": "0.0.0" } + }, + "Exports": { + "id": "Exports", + "path": "aws-cdk-lambda-destinations/Exports", + "children": { + "Output{\"Ref\":\"SnsSqsC4810B27\"}": { + "id": "Output{\"Ref\":\"SnsSqsC4810B27\"}", + "path": "aws-cdk-lambda-destinations/Exports/Output{\"Ref\":\"SnsSqsC4810B27\"}", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + }, + "Output{\"Ref\":\"Queue4A7E3555\"}": { + "id": "Output{\"Ref\":\"Queue4A7E3555\"}", + "path": "aws-cdk-lambda-destinations/Exports/Output{\"Ref\":\"Queue4A7E3555\"}", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } } }, "constructInfo": { "fqn": "@aws-cdk/core.Stack", "version": "0.0.0" } + }, + "Integ": { + "id": "Integ", + "path": "Integ", + "children": { + "DefaultTestCase": { + "id": "DefaultTestCase", + "path": "Integ/DefaultTestCase", + "children": { + "DeployAssert": { + "id": "DeployAssert", + "path": "Integ/DefaultTestCase/DeployAssert", + "children": { + "Default": { + "id": "Default", + "path": "Integ/DefaultTestCase/DeployAssert/Default", + "children": { + "LambdaInvoked12df417a1b74909abb3ea643735a310": { + "id": "LambdaInvoked12df417a1b74909abb3ea643735a310", + "path": "Integ/DefaultTestCase/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310", + "children": { + "SdkProvider": { + "id": "SdkProvider", + "path": "Integ/DefaultTestCase/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/SdkProvider", + "children": { + "AssertionsProvider": { + "id": "AssertionsProvider", + "path": "Integ/DefaultTestCase/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/SdkProvider/AssertionsProvider", + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AssertionsProvider", + "version": "0.0.0" + } + }, + "Default": { + "id": "Default", + "path": "Integ/DefaultTestCase/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/Default", + "children": { + "Default": { + "id": "Default", + "path": "Integ/DefaultTestCase/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/Default/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + }, + "Invoke": { + "id": "Invoke", + "path": "Integ/DefaultTestCase/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/Invoke", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.LambdaInvokeFunction", + "version": "0.0.0" + } + }, + "SdkQuerySQSreceiveMessage": { + "id": "SdkQuerySQSreceiveMessage", + "path": "Integ/DefaultTestCase/DeployAssert/Default/SdkQuerySQSreceiveMessage", + "children": { + "SdkProvider": { + "id": "SdkProvider", + "path": "Integ/DefaultTestCase/DeployAssert/Default/SdkQuerySQSreceiveMessage/SdkProvider", + "children": { + "AssertionsProvider": { + "id": "AssertionsProvider", + "path": "Integ/DefaultTestCase/DeployAssert/Default/SdkQuerySQSreceiveMessage/SdkProvider/AssertionsProvider", + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AssertionsProvider", + "version": "0.0.0" + } + }, + "Default": { + "id": "Default", + "path": "Integ/DefaultTestCase/DeployAssert/Default/SdkQuerySQSreceiveMessage/Default", + "children": { + "Default": { + "id": "Default", + "path": "Integ/DefaultTestCase/DeployAssert/Default/SdkQuerySQSreceiveMessage/Default/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.SdkQuery", + "version": "0.0.0" + } + }, + "ReceiveMessage": { + "id": "ReceiveMessage", + "path": "Integ/DefaultTestCase/DeployAssert/Default/ReceiveMessage", + "children": { + "AssertionProvider": { + "id": "AssertionProvider", + "path": "Integ/DefaultTestCase/DeployAssert/Default/ReceiveMessage/AssertionProvider", + "children": { + "AssertionsProvider": { + "id": "AssertionsProvider", + "path": "Integ/DefaultTestCase/DeployAssert/Default/ReceiveMessage/AssertionProvider/AssertionsProvider", + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AssertionsProvider", + "version": "0.0.0" + } + }, + "Default": { + "id": "Default", + "path": "Integ/DefaultTestCase/DeployAssert/Default/ReceiveMessage/Default", + "children": { + "Default": { + "id": "Default", + "path": "Integ/DefaultTestCase/DeployAssert/Default/ReceiveMessage/Default/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + }, + "AssertionResults": { + "id": "AssertionResults", + "path": "Integ/DefaultTestCase/DeployAssert/Default/ReceiveMessage/AssertionResults", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.EqualsAssertion", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.DeployAssert", + "version": "0.0.0" + } + }, + "SingletonFunction1488541a7b23466481b69b4408076b81": { + "id": "SingletonFunction1488541a7b23466481b69b4408076b81", + "path": "Integ/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81", + "children": { + "Staging": { + "id": "Staging", + "path": "Integ/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Staging", + "constructInfo": { + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "Integ/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + }, + "Handler": { + "id": "Handler", + "path": "Integ/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + }, + "AssetParameters": { + "id": "AssetParameters", + "path": "Integ/DefaultTestCase/DeployAssert/AssetParameters", + "children": { + "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0": { + "id": "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", + "path": "Integ/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", + "children": { + "S3Bucket": { + "id": "S3Bucket", + "path": "Integ/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3Bucket", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "S3VersionKey": { + "id": "S3VersionKey", + "path": "Integ/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3VersionKey", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "ArtifactHash": { + "id": "ArtifactHash", + "path": "Integ/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/ArtifactHash", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } } }, "constructInfo": { diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts b/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts index e505b441c52c1..ad6c48384c9b6 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts +++ b/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts @@ -4,6 +4,7 @@ import * as sqs from '@aws-cdk/aws-sqs'; import { App, Duration, Stack, StackProps } from '@aws-cdk/core'; import { Construct } from 'constructs'; import * as destinations from '../lib'; +import { IntegTest, InvocationType, EqualsAssertion, AssertionType, ActualResult, ExpectedResult } from '@aws-cdk/integ-tests'; /* * Stack verification steps: @@ -12,21 +13,23 @@ import * as destinations from '../lib'; */ class TestStack extends Stack { + public readonly fn: lambda.Function; + public readonly queue: sqs.Queue; constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); const topic = new sns.Topic(this, 'Topic'); - const queue = new sqs.Queue(this, 'Queue'); + this.queue = new sqs.Queue(this, 'Queue'); - const fn = new lambda.Function(this, 'SnsSqs', { + this.fn = new lambda.Function(this, 'SnsSqs', { runtime: lambda.Runtime.NODEJS_14_X, handler: 'index.handler', code: lambda.Code.fromInline(`exports.handler = async (event) => { - if (event === 'OK') return 'success'; + if (event.status === 'OK') return 'success'; throw new Error('failure'); };`), onFailure: new destinations.SnsDestination(topic), - onSuccess: new destinations.SqsDestination(queue), + onSuccess: new destinations.SqsDestination(this.queue), maxEventAge: Duration.hours(3), retryAttempts: 1, }); @@ -43,19 +46,19 @@ class TestStack extends Stack { runtime: lambda.Runtime.NODEJS_14_X, handler: 'index.handler', code: lambda.Code.fromInline(`exports.handler = async (event) => { - if (event === 'OK') return 'success'; + if (event.status === 'OK') return 'success'; throw new Error('failure'); };`), onFailure: new destinations.EventBridgeDestination(), onSuccess: new destinations.LambdaDestination(onSuccessLambda), }); - const version = fn.addVersion('MySpecialVersion'); + const version = this.fn.addVersion('MySpecialVersion'); new lambda.Alias(this, 'MySpecialAlias', { aliasName: 'MySpecialAlias', version, - onSuccess: new destinations.SqsDestination(queue), + onSuccess: new destinations.SqsDestination(this.queue), onFailure: new destinations.SnsDestination(topic), maxEventAge: Duration.hours(2), retryAttempts: 0, @@ -65,6 +68,37 @@ class TestStack extends Stack { const app = new App(); -new TestStack(app, 'aws-cdk-lambda-destinations'); +const stack = new TestStack(app, 'aws-cdk-lambda-destinations'); +const integ = new IntegTest(app, 'Integ', { + testCases: [stack], +}); + +integ.assert.invokeFunction({ + functionName: stack.fn.functionName, + invocationType: InvocationType.EVENT, + payload: JSON.stringify({ status: 'OK' }), +}); + +const message = integ.assert.awsApiCall('SQS', 'receiveMessage', { + QueueUrl: stack.queue.queueUrl, + WaitTimeSeconds: 20, +}); + +new EqualsAssertion(integ.assert, 'ReceiveMessage', { + actual: ActualResult.fromSdkQuery(message, 'Messages.0.Body'), + expected: ExpectedResult.fromObject({ + requestContext: { + condition: 'Success', + }, + requestPayload: { + status: 'OK', + }, + responseContext: { + statusCode: 200, + }, + responsePayload: 'success', + }), + assertionType: AssertionType.OBJECT_LIKE, +}); app.synth(); diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index 2e92d5cb4848d..3226119785d2b 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -84,6 +84,7 @@ "license": "Apache-2.0", "devDependencies": { "@aws-cdk/assertions": "0.0.0", + "@aws-cdk/integ-tests": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/integ-runner": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/IntegTestDefaultTestCaseDeployAssert4F885E41.template.json b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/IntegTestDefaultTestCaseDeployAssert4F885E41.template.json new file mode 100644 index 0000000000000..0487b84a86aad --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/IntegTestDefaultTestCaseDeployAssert4F885E41.template.json @@ -0,0 +1,211 @@ +{ + "Resources": { + "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8": { + "Type": "Custom::DeployAssert@SdkCallLambdainvoke", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F", + "Arn" + ] + }, + "service": "Lambda", + "api": "invoke", + "parameters": { + "FunctionName": { + "Fn::ImportValue": "cdk-integ-lambda-bundling:ExportsOutputRefFunction76856677C48862D5" + } + }, + "flattenResponse": "false", + "salt": "1651497936527" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8InvokeA3F6E40A": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::ImportValue": "cdk-integ-lambda-bundling:ExportsOutputRefFunction76856677C48862D5" + }, + "Principal": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73", + "Arn" + ] + } + } + }, + "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8EqualsAssertionLambdainvoke4FA91B10": { + "Type": "Custom::DeployAssert@AssertEquals", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F", + "Arn" + ] + }, + "actual": { + "Fn::GetAtt": [ + "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8", + "apiCallResponse" + ] + }, + "expected": "{\"Payload\":\"200\"}", + "assertionType": "objectLike", + "salt": "1651497936528" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "lambda:Invoke" + ], + "Effect": "Allow", + "Resource": [ + "*" + ] + }, + { + "Action": [ + "lambda:InvokeFunction" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":lambda:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":function:", + { + "Fn::ImportValue": "cdk-integ-lambda-bundling:ExportsOutputRefFunction76856677C48862D5" + } + ] + ] + } + ] + } + ] + } + } + ] + } + }, + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Runtime": "nodejs14.x", + "Code": { + "S3Bucket": { + "Ref": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4" + } + ] + } + ] + } + ] + ] + } + }, + "Timeout": 120, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73", + "Arn" + ] + } + } + } + }, + "Outputs": { + "AssertionResultsEqualsAssertionLambdainvoke": { + "Value": { + "Fn::GetAtt": [ + "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8EqualsAssertionLambdainvoke4FA91B10", + "data" + ] + } + } + }, + "Parameters": { + "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB": { + "Type": "String", + "Description": "S3 bucket for asset \"37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0\"" + }, + "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4": { + "Type": "String", + "Description": "S3 key for asset version \"37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0\"" + }, + "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0ArtifactHash4C05AFBC": { + "Type": "String", + "Description": "Artifact hash for asset \"37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0\"" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/cdk-integ-lambda-bundling.template.json b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/cdk-integ-lambda-bundling.template.json index 33783d01d4fa5..836c76f7cc95e 100644 --- a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/cdk-integ-lambda-bundling.template.json +++ b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/cdk-integ-lambda-bundling.template.json @@ -36,7 +36,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersfec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509S3BucketBF50F97C" + "Ref": "AssetParametersb0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374eS3Bucket305E1975" }, "S3Key": { "Fn::Join": [ @@ -49,7 +49,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersfec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509S3VersionKeyF21AC8C1" + "Ref": "AssetParametersb0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374eS3VersionKeyCC928AE5" } ] } @@ -62,7 +62,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersfec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509S3VersionKeyF21AC8C1" + "Ref": "AssetParametersb0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374eS3VersionKeyCC928AE5" } ] } @@ -79,7 +79,7 @@ ] }, "Handler": "index.handler", - "Runtime": "python3.6" + "Runtime": "python3.9" }, "DependsOn": [ "FunctionServiceRole675BB04A" @@ -87,26 +87,26 @@ } }, "Parameters": { - "AssetParametersfec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509S3BucketBF50F97C": { + "AssetParametersb0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374eS3Bucket305E1975": { "Type": "String", - "Description": "S3 bucket for asset \"fec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509\"" + "Description": "S3 bucket for asset \"b0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374e\"" }, - "AssetParametersfec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509S3VersionKeyF21AC8C1": { + "AssetParametersb0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374eS3VersionKeyCC928AE5": { "Type": "String", - "Description": "S3 key for asset version \"fec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509\"" + "Description": "S3 key for asset version \"b0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374e\"" }, - "AssetParametersfec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509ArtifactHash5D8C129B": { + "AssetParametersb0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374eArtifactHashBE058EE4": { "Type": "String", - "Description": "Artifact hash for asset \"fec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509\"" + "Description": "Artifact hash for asset \"b0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374e\"" } }, "Outputs": { - "FunctionArn": { + "ExportsOutputRefFunction76856677C48862D5": { "Value": { - "Fn::GetAtt": [ - "Function76856677", - "Arn" - ] + "Ref": "Function76856677" + }, + "Export": { + "Name": "cdk-integ-lambda-bundling:ExportsOutputRefFunction76856677C48862D5" } } } diff --git a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/cdk.out index 90bef2e09ad39..2efc89439fab8 100644 --- a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"17.0.0"} \ No newline at end of file +{"version":"18.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/integ.json b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/integ.json index 5f0450b8a4c09..02690474d95f4 100644 --- a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/integ.json +++ b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/integ.json @@ -1,14 +1,12 @@ { "version": "18.0.0", "testCases": { - "aws-lambda/test/integ.bundling": { + "DefaultTestCase": { "stacks": [ "cdk-integ-lambda-bundling" ], - "diffAssets": false, - "stackUpdateWorkflow": false + "stackUpdateWorkflow": false, + "assertionStack": "IntegTestDefaultTestCaseDeployAssert4F885E41" } - }, - "synthContext": {}, - "enableLookups": false + } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/manifest.json index 360adb50592e9..c881060526e59 100644 --- a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "17.0.0", + "version": "18.0.0", "artifacts": { "Tree": { "type": "cdk:tree", @@ -19,13 +19,13 @@ { "type": "aws:cdk:asset", "data": { - "path": "asset.fec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509", - "id": "fec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509", + "path": "asset.b0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374e", + "id": "b0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374e", "packaging": "zip", - "sourceHash": "fec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509", - "s3BucketParameter": "AssetParametersfec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509S3BucketBF50F97C", - "s3KeyParameter": "AssetParametersfec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509S3VersionKeyF21AC8C1", - "artifactHashParameter": "AssetParametersfec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509ArtifactHash5D8C129B" + "sourceHash": "b0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374e", + "s3BucketParameter": "AssetParametersb0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374eS3Bucket305E1975", + "s3KeyParameter": "AssetParametersb0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374eS3VersionKeyCC928AE5", + "artifactHashParameter": "AssetParametersb0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374eArtifactHashBE058EE4" } } ], @@ -41,32 +41,114 @@ "data": "Function76856677" } ], - "/cdk-integ-lambda-bundling/AssetParameters/fec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509/S3Bucket": [ + "/cdk-integ-lambda-bundling/AssetParameters/b0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374e/S3Bucket": [ { "type": "aws:cdk:logicalId", - "data": "AssetParametersfec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509S3BucketBF50F97C" + "data": "AssetParametersb0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374eS3Bucket305E1975" } ], - "/cdk-integ-lambda-bundling/AssetParameters/fec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509/S3VersionKey": [ + "/cdk-integ-lambda-bundling/AssetParameters/b0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374e/S3VersionKey": [ { "type": "aws:cdk:logicalId", - "data": "AssetParametersfec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509S3VersionKeyF21AC8C1" + "data": "AssetParametersb0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374eS3VersionKeyCC928AE5" } ], - "/cdk-integ-lambda-bundling/AssetParameters/fec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509/ArtifactHash": [ + "/cdk-integ-lambda-bundling/AssetParameters/b0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374e/ArtifactHash": [ { "type": "aws:cdk:logicalId", - "data": "AssetParametersfec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509ArtifactHash5D8C129B" + "data": "AssetParametersb0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374eArtifactHashBE058EE4" } ], - "/cdk-integ-lambda-bundling/FunctionArn": [ + "/cdk-integ-lambda-bundling/Exports/Output{\"Ref\":\"Function76856677\"}": [ { "type": "aws:cdk:logicalId", - "data": "FunctionArn" + "data": "ExportsOutputRefFunction76856677C48862D5" } ] }, "displayName": "cdk-integ-lambda-bundling" + }, + "IntegTestDefaultTestCaseDeployAssert4F885E41": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "IntegTestDefaultTestCaseDeployAssert4F885E41.template.json", + "validateOnSynth": false + }, + "dependencies": [ + "cdk-integ-lambda-bundling" + ], + "metadata": { + "/IntegTest/DefaultTestCase/DeployAssert": [ + { + "type": "aws:cdk:asset", + "data": { + "path": "asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle", + "id": "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", + "packaging": "zip", + "sourceHash": "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", + "s3BucketParameter": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB", + "s3KeyParameter": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4", + "artifactHashParameter": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0ArtifactHash4C05AFBC" + } + } + ], + "/IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8" + } + ], + "/IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/Invoke": [ + { + "type": "aws:cdk:logicalId", + "data": "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8InvokeA3F6E40A" + } + ], + "/IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/EqualsAssertionLambdainvoke/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8EqualsAssertionLambdainvoke4FA91B10" + } + ], + "/IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/EqualsAssertionLambdainvoke/AssertionResults": [ + { + "type": "aws:cdk:logicalId", + "data": "AssertionResultsEqualsAssertionLambdainvoke" + } + ], + "/IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73" + } + ], + "/IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F" + } + ], + "/IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3Bucket": [ + { + "type": "aws:cdk:logicalId", + "data": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB" + } + ], + "/IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3VersionKey": [ + { + "type": "aws:cdk:logicalId", + "data": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4" + } + ], + "/IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/ArtifactHash": [ + { + "type": "aws:cdk:logicalId", + "data": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0ArtifactHash4C05AFBC" + } + ] + }, + "displayName": "IntegTest/DefaultTestCase/DeployAssert" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/tree.json b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/tree.json index 032800c2d957a..ffd7f7cd73615 100644 --- a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/tree.json @@ -103,7 +103,7 @@ "aws:cdk:cloudformation:props": { "code": { "s3Bucket": { - "Ref": "AssetParametersfec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509S3BucketBF50F97C" + "Ref": "AssetParametersb0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374eS3Bucket305E1975" }, "s3Key": { "Fn::Join": [ @@ -116,7 +116,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersfec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509S3VersionKeyF21AC8C1" + "Ref": "AssetParametersb0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374eS3VersionKeyCC928AE5" } ] } @@ -129,7 +129,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersfec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509S3VersionKeyF21AC8C1" + "Ref": "AssetParametersb0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374eS3VersionKeyCC928AE5" } ] } @@ -146,7 +146,7 @@ ] }, "handler": "index.handler", - "runtime": "python3.6" + "runtime": "python3.9" } }, "constructInfo": { @@ -164,13 +164,13 @@ "id": "AssetParameters", "path": "cdk-integ-lambda-bundling/AssetParameters", "children": { - "fec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509": { - "id": "fec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509", - "path": "cdk-integ-lambda-bundling/AssetParameters/fec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509", + "b0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374e": { + "id": "b0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374e", + "path": "cdk-integ-lambda-bundling/AssetParameters/b0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374e", "children": { "S3Bucket": { "id": "S3Bucket", - "path": "cdk-integ-lambda-bundling/AssetParameters/fec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509/S3Bucket", + "path": "cdk-integ-lambda-bundling/AssetParameters/b0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374e/S3Bucket", "constructInfo": { "fqn": "@aws-cdk/core.CfnParameter", "version": "0.0.0" @@ -178,7 +178,7 @@ }, "S3VersionKey": { "id": "S3VersionKey", - "path": "cdk-integ-lambda-bundling/AssetParameters/fec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509/S3VersionKey", + "path": "cdk-integ-lambda-bundling/AssetParameters/b0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374e/S3VersionKey", "constructInfo": { "fqn": "@aws-cdk/core.CfnParameter", "version": "0.0.0" @@ -186,7 +186,7 @@ }, "ArtifactHash": { "id": "ArtifactHash", - "path": "cdk-integ-lambda-bundling/AssetParameters/fec1c56a3f23d9d27f58815e0c34c810cc02f431ac63a078f9b5d2aa44cc3509/ArtifactHash", + "path": "cdk-integ-lambda-bundling/AssetParameters/b0011b8704c0cceee88b3cdf79d915b7babbe192f420c472879803f44c2c374e/ArtifactHash", "constructInfo": { "fqn": "@aws-cdk/core.CfnParameter", "version": "0.0.0" @@ -204,11 +204,21 @@ "version": "0.0.0" } }, - "FunctionArn": { - "id": "FunctionArn", - "path": "cdk-integ-lambda-bundling/FunctionArn", + "Exports": { + "id": "Exports", + "path": "cdk-integ-lambda-bundling/Exports", + "children": { + "Output{\"Ref\":\"Function76856677\"}": { + "id": "Output{\"Ref\":\"Function76856677\"}", + "path": "cdk-integ-lambda-bundling/Exports/Output{\"Ref\":\"Function76856677\"}", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + } + }, "constructInfo": { - "fqn": "@aws-cdk/core.CfnOutput", + "fqn": "@aws-cdk/core.Construct", "version": "0.0.0" } } @@ -217,6 +227,232 @@ "fqn": "@aws-cdk/core.Stack", "version": "0.0.0" } + }, + "IntegTest": { + "id": "IntegTest", + "path": "IntegTest", + "children": { + "DefaultTestCase": { + "id": "DefaultTestCase", + "path": "IntegTest/DefaultTestCase", + "children": { + "DeployAssert": { + "id": "DeployAssert", + "path": "IntegTest/DefaultTestCase/DeployAssert", + "children": { + "Default": { + "id": "Default", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default", + "children": { + "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8": { + "id": "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8", + "children": { + "SdkProvider": { + "id": "SdkProvider", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/SdkProvider", + "children": { + "AssertionsProvider": { + "id": "AssertionsProvider", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/SdkProvider/AssertionsProvider", + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AssertionsProvider", + "version": "0.0.0" + } + }, + "Default": { + "id": "Default", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/Default", + "children": { + "Default": { + "id": "Default", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/Default/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + }, + "Invoke": { + "id": "Invoke", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/Invoke", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + }, + "EqualsAssertionLambdainvoke": { + "id": "EqualsAssertionLambdainvoke", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/EqualsAssertionLambdainvoke", + "children": { + "AssertionProvider": { + "id": "AssertionProvider", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/EqualsAssertionLambdainvoke/AssertionProvider", + "children": { + "AssertionsProvider": { + "id": "AssertionsProvider", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/EqualsAssertionLambdainvoke/AssertionProvider/AssertionsProvider", + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AssertionsProvider", + "version": "0.0.0" + } + }, + "Default": { + "id": "Default", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/EqualsAssertionLambdainvoke/Default", + "children": { + "Default": { + "id": "Default", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/EqualsAssertionLambdainvoke/Default/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + }, + "AssertionResults": { + "id": "AssertionResults", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/EqualsAssertionLambdainvoke/AssertionResults", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.EqualsAssertion", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.LambdaInvokeFunction", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.DeployAssert", + "version": "0.0.0" + } + }, + "SingletonFunction1488541a7b23466481b69b4408076b81": { + "id": "SingletonFunction1488541a7b23466481b69b4408076b81", + "path": "IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81", + "children": { + "Staging": { + "id": "Staging", + "path": "IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Staging", + "constructInfo": { + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + }, + "Handler": { + "id": "Handler", + "path": "IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + }, + "AssetParameters": { + "id": "AssetParameters", + "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters", + "children": { + "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0": { + "id": "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", + "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", + "children": { + "S3Bucket": { + "id": "S3Bucket", + "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3Bucket", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "S3VersionKey": { + "id": "S3VersionKey", + "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3VersionKey", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "ArtifactHash": { + "id": "ArtifactHash", + "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/ArtifactHash", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } } }, "constructInfo": { diff --git a/packages/@aws-cdk/aws-lambda/test/integ.bundling.ts b/packages/@aws-cdk/aws-lambda/test/integ.bundling.ts index e25ee277c7ed4..c12b700ba3fbd 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.bundling.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.bundling.ts @@ -1,6 +1,7 @@ /// !cdk-integ pragma:disable-update-workflow import * as path from 'path'; -import { App, CfnOutput, Stack, StackProps } from '@aws-cdk/core'; +import { App, Stack, StackProps } from '@aws-cdk/core'; +import { IntegTest } from '@aws-cdk/integ-tests'; import { Construct } from 'constructs'; import * as lambda from '../lib'; @@ -12,6 +13,7 @@ import * as lambda from '../lib'; * The last command should show '200' */ class TestStack extends Stack { + public readonly functionName: string; constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); @@ -19,7 +21,7 @@ class TestStack extends Stack { const fn = new lambda.Function(this, 'Function', { code: lambda.Code.fromAsset(assetPath, { bundling: { - image: lambda.Runtime.PYTHON_3_6.bundlingImage, + image: lambda.Runtime.PYTHON_3_9.bundlingImage, command: [ 'bash', '-c', [ 'cp -au . /asset-output', @@ -29,16 +31,26 @@ class TestStack extends Stack { ], }, }), - runtime: lambda.Runtime.PYTHON_3_6, + runtime: lambda.Runtime.PYTHON_3_9, handler: 'index.handler', }); - new CfnOutput(this, 'FunctionArn', { - value: fn.functionArn, - }); + this.functionName = fn.functionName; } } const app = new App(); -new TestStack(app, 'cdk-integ-lambda-bundling'); +const stack = new TestStack(app, 'cdk-integ-lambda-bundling'); + +const integ = new IntegTest(app, 'IntegTest', { + testCases: [stack], + stackUpdateWorkflow: false, +}); + +const invoke = integ.assert.invokeFunction({ + functionName: stack.functionName, +}); +invoke.assertObjectLike({ + Payload: '200', +}); app.synth(); diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/package.json b/packages/@aws-cdk/aws-stepfunctions-tasks/package.json index 94e63879e0032..4831f4c18845c 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/package.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/package.json @@ -78,6 +78,7 @@ }, "license": "Apache-2.0", "devDependencies": { + "@aws-cdk/integ-tests": "0.0.0", "@aws-cdk/assertions": "0.0.0", "@aws-cdk/aws-apigatewayv2": "0.0.0", "@aws-cdk/aws-apigatewayv2-integrations": "0.0.0", diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/integ.put-events.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/integ.put-events.ts index 84198e2df0b45..8e4664099283e 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/integ.put-events.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/integ.put-events.ts @@ -1,6 +1,7 @@ import * as events from '@aws-cdk/aws-events'; import * as sfn from '@aws-cdk/aws-stepfunctions'; import * as cdk from '@aws-cdk/core'; +import { IntegTest } from '@aws-cdk/integ-tests'; import { EventBridgePutEvents } from '../../lib'; /* @@ -39,8 +40,24 @@ const sm = new sfn.StateMachine(stack, 'StateMachine', { timeout: cdk.Duration.seconds(30), }); -new cdk.CfnOutput(stack, 'stateMachineArn', { - value: sm.stateMachineArn, + +const testCase = new IntegTest(app, 'IntegTest', { + testCases: [stack], +}); + +// Start an execution +const start = testCase.assert.awsApiCall('StepFunctions', 'startExecution', { + stateMachineArn: sm.stateMachineArn, +}); + +// describe the results of the execution +const describe = testCase.assert.awsApiCall('StepFunctions', 'describeExecution', { + executionArn: start.getAttString('executionArn'), +}); + +// assert the results +describe.assertObjectLike({ + status: 'SUCCEEDED', }); app.synth(); diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/IntegTestDefaultTestCaseDeployAssert4F885E41.template.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/IntegTestDefaultTestCaseDeployAssert4F885E41.template.json new file mode 100644 index 0000000000000..f130c19d1729d --- /dev/null +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/IntegTestDefaultTestCaseDeployAssert4F885E41.template.json @@ -0,0 +1,199 @@ +{ + "Resources": { + "SdkQueryStepFunctionsstartExecution": { + "Type": "Custom::DeployAssert@SdkCallStepFunctionsstartExecution", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F", + "Arn" + ] + }, + "service": "StepFunctions", + "api": "startExecution", + "parameters": { + "stateMachineArn": { + "Fn::ImportValue": "aws-stepfunctions-tasks-eventbridge-put-events-integ:ExportsOutputRefStateMachine2E01A3A5BA46F753" + } + }, + "flattenResponse": "true", + "salt": "1651498056105" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "SdkQueryStepFunctionsdescribeExecution": { + "Type": "Custom::DeployAssert@SdkCallStepFunctionsdescribeExecution", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F", + "Arn" + ] + }, + "service": "StepFunctions", + "api": "describeExecution", + "parameters": { + "executionArn": { + "Fn::GetAtt": [ + "SdkQueryStepFunctionsstartExecution", + "apiCallResponse.executionArn" + ] + } + }, + "flattenResponse": "false", + "salt": "1651498056106" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "SdkQueryStepFunctionsdescribeExecutionEqualsAssertionStepFunctionsdescribeExecutionA4C8D5D7": { + "Type": "Custom::DeployAssert@AssertEquals", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F", + "Arn" + ] + }, + "actual": { + "Fn::GetAtt": [ + "SdkQueryStepFunctionsdescribeExecution", + "apiCallResponse" + ] + }, + "expected": "{\"status\":\"SUCCEEDED\"}", + "assertionType": "objectLike", + "salt": "1651498056107" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "states:StartExecution" + ], + "Effect": "Allow", + "Resource": [ + "*" + ] + }, + { + "Action": [ + "states:DescribeExecution" + ], + "Effect": "Allow", + "Resource": [ + "*" + ] + } + ] + } + } + ] + } + }, + "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Runtime": "nodejs14.x", + "Code": { + "S3Bucket": { + "Ref": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4" + } + ] + } + ] + } + ] + ] + } + }, + "Timeout": 120, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73", + "Arn" + ] + } + } + } + }, + "Outputs": { + "AssertionResultsEqualsAssertionStepFunctionsdescribeExecution": { + "Value": { + "Fn::GetAtt": [ + "SdkQueryStepFunctionsdescribeExecutionEqualsAssertionStepFunctionsdescribeExecutionA4C8D5D7", + "data" + ] + } + } + }, + "Parameters": { + "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB": { + "Type": "String", + "Description": "S3 bucket for asset \"37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0\"" + }, + "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4": { + "Type": "String", + "Description": "S3 key for asset version \"37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0\"" + }, + "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0ArtifactHash4C05AFBC": { + "Type": "String", + "Description": "Artifact hash for asset \"37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0\"" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle/index.js b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle/index.js new file mode 100644 index 0000000000000..3a860016dea59 --- /dev/null +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle/index.js @@ -0,0 +1,643 @@ +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getOwnPropSymbols = Object.getOwnPropertySymbols; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __propIsEnum = Object.prototype.propertyIsEnumerable; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __spreadValues = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + if (__getOwnPropSymbols) + for (var prop of __getOwnPropSymbols(b)) { + if (__propIsEnum.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + } + return a; +}; +var __esm = (fn, res) => function __init() { + return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// ../assertions/lib/matcher.ts +var Matcher, MatchResult; +var init_matcher = __esm({ + "../assertions/lib/matcher.ts"() { + Matcher = class { + static isMatcher(x) { + return x && x instanceof Matcher; + } + }; + MatchResult = class { + constructor(target) { + this.failures = []; + this.captures = /* @__PURE__ */ new Map(); + this.finalized = false; + this.target = target; + } + push(matcher, path, message) { + return this.recordFailure({ matcher, path, message }); + } + recordFailure(failure) { + this.failures.push(failure); + return this; + } + hasFailed() { + return this.failures.length !== 0; + } + get failCount() { + return this.failures.length; + } + compose(id, inner) { + const innerF = inner.failures; + this.failures.push(...innerF.map((f) => { + return { path: [id, ...f.path], message: f.message, matcher: f.matcher }; + })); + inner.captures.forEach((vals, capture) => { + vals.forEach((value) => this.recordCapture({ capture, value })); + }); + return this; + } + finished() { + if (this.finalized) { + return this; + } + if (this.failCount === 0) { + this.captures.forEach((vals, cap) => cap._captured.push(...vals)); + } + this.finalized = true; + return this; + } + toHumanStrings() { + return this.failures.map((r) => { + const loc = r.path.length === 0 ? "" : ` at ${r.path.join("")}`; + return "" + r.message + loc + ` (using ${r.matcher.name} matcher)`; + }); + } + recordCapture(options) { + let values = this.captures.get(options.capture); + if (values === void 0) { + values = []; + } + values.push(options.value); + this.captures.set(options.capture, values); + } + }; + } +}); + +// ../assertions/lib/private/matchers/absent.ts +var AbsentMatch; +var init_absent = __esm({ + "../assertions/lib/private/matchers/absent.ts"() { + init_matcher(); + AbsentMatch = class extends Matcher { + constructor(name) { + super(); + this.name = name; + } + test(actual) { + const result = new MatchResult(actual); + if (actual !== void 0) { + result.recordFailure({ + matcher: this, + path: [], + message: `Received ${actual}, but key should be absent` + }); + } + return result; + } + }; + } +}); + +// ../assertions/lib/private/type.ts +function getType(obj) { + return Array.isArray(obj) ? "array" : typeof obj; +} +var init_type = __esm({ + "../assertions/lib/private/type.ts"() { + } +}); + +// ../assertions/lib/match.ts +var match_exports = {}; +__export(match_exports, { + Match: () => Match +}); +var Match, LiteralMatch, ArrayMatch, ObjectMatch, SerializedJson, NotMatch, AnyMatch, StringLikeRegexpMatch; +var init_match = __esm({ + "../assertions/lib/match.ts"() { + init_matcher(); + init_absent(); + init_type(); + Match = class { + static absent() { + return new AbsentMatch("absent"); + } + static arrayWith(pattern) { + return new ArrayMatch("arrayWith", pattern); + } + static arrayEquals(pattern) { + return new ArrayMatch("arrayEquals", pattern, { subsequence: false }); + } + static exact(pattern) { + return new LiteralMatch("exact", pattern, { partialObjects: false }); + } + static objectLike(pattern) { + return new ObjectMatch("objectLike", pattern); + } + static objectEquals(pattern) { + return new ObjectMatch("objectEquals", pattern, { partial: false }); + } + static not(pattern) { + return new NotMatch("not", pattern); + } + static serializedJson(pattern) { + return new SerializedJson("serializedJson", pattern); + } + static anyValue() { + return new AnyMatch("anyValue"); + } + static stringLikeRegexp(pattern) { + return new StringLikeRegexpMatch("stringLikeRegexp", pattern); + } + }; + LiteralMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.partialObjects = options.partialObjects ?? false; + if (Matcher.isMatcher(this.pattern)) { + throw new Error("LiteralMatch cannot directly contain another matcher. Remove the top-level matcher or nest it more deeply."); + } + } + test(actual) { + if (Array.isArray(this.pattern)) { + return new ArrayMatch(this.name, this.pattern, { subsequence: false, partialObjects: this.partialObjects }).test(actual); + } + if (typeof this.pattern === "object") { + return new ObjectMatch(this.name, this.pattern, { partial: this.partialObjects }).test(actual); + } + const result = new MatchResult(actual); + if (typeof this.pattern !== typeof actual) { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected type ${typeof this.pattern} but received ${getType(actual)}` + }); + return result; + } + if (actual !== this.pattern) { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected ${this.pattern} but received ${actual}` + }); + } + return result; + } + }; + ArrayMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.subsequence = options.subsequence ?? true; + this.partialObjects = options.partialObjects ?? false; + } + test(actual) { + if (!Array.isArray(actual)) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected type array but received ${getType(actual)}` + }); + } + if (!this.subsequence && this.pattern.length !== actual.length) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected array of length ${this.pattern.length} but received ${actual.length}` + }); + } + let patternIdx = 0; + let actualIdx = 0; + const result = new MatchResult(actual); + while (patternIdx < this.pattern.length && actualIdx < actual.length) { + const patternElement = this.pattern[patternIdx]; + const matcher = Matcher.isMatcher(patternElement) ? patternElement : new LiteralMatch(this.name, patternElement, { partialObjects: this.partialObjects }); + const matcherName = matcher.name; + if (this.subsequence && (matcherName == "absent" || matcherName == "anyValue")) { + throw new Error(`The Matcher ${matcherName}() cannot be nested within arrayWith()`); + } + const innerResult = matcher.test(actual[actualIdx]); + if (!this.subsequence || !innerResult.hasFailed()) { + result.compose(`[${actualIdx}]`, innerResult); + patternIdx++; + actualIdx++; + } else { + actualIdx++; + } + } + for (; patternIdx < this.pattern.length; patternIdx++) { + const pattern = this.pattern[patternIdx]; + const element = Matcher.isMatcher(pattern) || typeof pattern === "object" ? " " : ` [${pattern}] `; + result.recordFailure({ + matcher: this, + path: [], + message: `Missing element${element}at pattern index ${patternIdx}` + }); + } + return result; + } + }; + ObjectMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.partial = options.partial ?? true; + } + test(actual) { + if (typeof actual !== "object" || Array.isArray(actual)) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected type object but received ${getType(actual)}` + }); + } + const result = new MatchResult(actual); + if (!this.partial) { + for (const a of Object.keys(actual)) { + if (!(a in this.pattern)) { + result.recordFailure({ + matcher: this, + path: [`/${a}`], + message: "Unexpected key" + }); + } + } + } + for (const [patternKey, patternVal] of Object.entries(this.pattern)) { + if (!(patternKey in actual) && !(patternVal instanceof AbsentMatch)) { + result.recordFailure({ + matcher: this, + path: [`/${patternKey}`], + message: `Missing key '${patternKey}' among {${Object.keys(actual).join(",")}}` + }); + continue; + } + const matcher = Matcher.isMatcher(patternVal) ? patternVal : new LiteralMatch(this.name, patternVal, { partialObjects: this.partial }); + const inner = matcher.test(actual[patternKey]); + result.compose(`/${patternKey}`, inner); + } + return result; + } + }; + SerializedJson = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const result = new MatchResult(actual); + if (getType(actual) !== "string") { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected JSON as a string but found ${getType(actual)}` + }); + return result; + } + let parsed; + try { + parsed = JSON.parse(actual); + } catch (err) { + if (err instanceof SyntaxError) { + result.recordFailure({ + matcher: this, + path: [], + message: `Invalid JSON string: ${actual}` + }); + return result; + } else { + throw err; + } + } + const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); + const innerResult = matcher.test(parsed); + result.compose(`(${this.name})`, innerResult); + return result; + } + }; + NotMatch = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); + const innerResult = matcher.test(actual); + const result = new MatchResult(actual); + if (innerResult.failCount === 0) { + result.recordFailure({ + matcher: this, + path: [], + message: `Found unexpected match: ${JSON.stringify(actual, void 0, 2)}` + }); + } + return result; + } + }; + AnyMatch = class extends Matcher { + constructor(name) { + super(); + this.name = name; + } + test(actual) { + const result = new MatchResult(actual); + if (actual == null) { + result.recordFailure({ + matcher: this, + path: [], + message: "Expected a value but found none" + }); + } + return result; + } + }; + StringLikeRegexpMatch = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const result = new MatchResult(actual); + const regex = new RegExp(this.pattern, "gm"); + if (typeof actual !== "string") { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected a string, but got '${typeof actual}'` + }); + } + if (!regex.test(actual)) { + result.recordFailure({ + matcher: this, + path: [], + message: `String '${actual}' did not match pattern '${this.pattern}'` + }); + } + return result; + } + }; + } +}); + +// lib/assertions/providers/lambda-handler/index.ts +var lambda_handler_exports = {}; +__export(lambda_handler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(lambda_handler_exports); + +// lib/assertions/providers/lambda-handler/base.ts +var https = __toESM(require("https")); +var url = __toESM(require("url")); +var CustomResourceHandler = class { + constructor(event, context) { + this.event = event; + this.context = context; + this.timedOut = false; + this.timeout = setTimeout(async () => { + await this.respond({ + status: "FAILED", + reason: "Lambda Function Timeout", + data: this.context.logStreamName + }); + this.timedOut = true; + }, context.getRemainingTimeInMillis() - 1200); + this.event = event; + this.physicalResourceId = extractPhysicalResourceId(event); + } + async handle() { + try { + console.log(`Event: ${JSON.stringify(this.event)}`); + const response = await this.processEvent(this.event.ResourceProperties); + console.log(`Event output : ${JSON.stringify(response)}`); + await this.respond({ + status: "SUCCESS", + reason: "OK", + data: response + }); + } catch (e) { + console.log(e); + await this.respond({ + status: "FAILED", + reason: e.message ?? "Internal Error" + }); + } finally { + clearTimeout(this.timeout); + } + } + respond(response) { + if (this.timedOut) { + return; + } + const cfResponse = { + Status: response.status, + Reason: response.reason, + PhysicalResourceId: this.physicalResourceId, + StackId: this.event.StackId, + RequestId: this.event.RequestId, + LogicalResourceId: this.event.LogicalResourceId, + NoEcho: false, + Data: response.data + }; + const responseBody = JSON.stringify(cfResponse); + console.log("Responding to CloudFormation", responseBody); + const parsedUrl = url.parse(this.event.ResponseURL); + const requestOptions = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: "PUT", + headers: { "content-type": "", "content-length": responseBody.length } + }; + return new Promise((resolve, reject) => { + try { + const request2 = https.request(requestOptions, resolve); + request2.on("error", reject); + request2.write(responseBody); + request2.end(); + } catch (e) { + reject(e); + } + }); + } +}; +function extractPhysicalResourceId(event) { + switch (event.RequestType) { + case "Create": + return event.LogicalResourceId; + case "Update": + case "Delete": + return event.PhysicalResourceId; + } +} + +// lib/assertions/providers/lambda-handler/assertion.ts +var { Match: Match2 } = (init_match(), __toCommonJS(match_exports)); +var AssertionHandler = class extends CustomResourceHandler { + async processEvent(request2) { + let actual = request2.actual; + const expected = decodeCall(request2.expected); + let result; + let matcher; + console.log(`Testing equality between ${JSON.stringify(request2.actual)} and ${JSON.stringify(request2.expected)}`); + switch (request2.assertionType) { + case "objectLike": + if (typeof actual === "string") { + actual = JSON.parse(actual); + } + matcher = Match2.objectLike(expected); + break; + case "equals": + matcher = Match2.exact(expected); + break; + case "arrayWith": + matcher = Match2.arrayWith(expected); + break; + default: + throw new Error(`Unsupported query type ${request2.assertionType}`); + } + const matchResult = matcher.test(actual); + matchResult.finished(); + if (matchResult.hasFailed()) { + result = { + data: JSON.stringify({ + status: "fail", + message: [ + ...matchResult.toHumanStrings(), + JSON.stringify(matchResult.target, void 0, 2) + ].join("\n") + }) + }; + } else { + result = { + data: JSON.stringify({ + status: "pass" + }) + }; + } + return result; + } +}; +function decodeCall(call) { + if (!call) { + return void 0; + } + return JSON.parse(call); +} + +// lib/assertions/providers/lambda-handler/results.ts +var ResultsCollectionHandler = class extends CustomResourceHandler { + async processEvent(request2) { + const reduced = request2.assertionResults.reduce((agg, result, idx) => { + const msg = result.status === "pass" ? "pass" : `fail - ${result.message}`; + return `${agg} +Test${idx}: ${msg}`; + }, "").trim(); + return { message: reduced }; + } +}; + +// lib/assertions/providers/lambda-handler/utils.ts +function decode(object) { + return JSON.parse(JSON.stringify(object), (_k, v) => { + switch (v) { + case "TRUE:BOOLEAN": + return true; + case "FALSE:BOOLEAN": + return false; + case !isNaN(+v): + return +v; + default: + return v; + } + }); +} + +// lib/assertions/providers/lambda-handler/sdk.ts +function flatten(object) { + return Object.assign({}, ...function _flatten(child, path = []) { + return [].concat(...Object.keys(child).map((key) => { + const childKey = Buffer.isBuffer(child[key]) ? child[key].toString("utf8") : child[key]; + return typeof childKey === "object" && childKey !== null ? _flatten(childKey, path.concat([key])) : { [path.concat([key]).join(".")]: childKey }; + })); + }(object)); +} +var SdkHandler = class extends CustomResourceHandler { + async processEvent(request2) { + const AWS = require("aws-sdk"); + console.log(`AWS SDK VERSION: ${AWS.VERSION}`); + const service = new AWS[request2.service](); + const response = await service[request2.api](request2.parameters && decode(request2.parameters)).promise(); + console.log(`SDK response received ${JSON.stringify(response)}`); + delete response.ResponseMetadata; + const respond = { + apiCallResponse: response + }; + const flatData = __spreadValues({}, flatten(respond)); + return request2.flattenResponse === "true" ? flatData : respond; + } +}; + +// lib/assertions/providers/lambda-handler/types.ts +var ASSERT_RESOURCE_TYPE = "Custom::DeployAssert@AssertEquals"; +var RESULTS_RESOURCE_TYPE = "Custom::DeployAssert@ResultsCollection"; +var SDK_RESOURCE_TYPE_PREFIX = "Custom::DeployAssert@SdkCall"; + +// lib/assertions/providers/lambda-handler/index.ts +async function handler(event, context) { + const provider = createResourceHandler(event, context); + await provider.handle(); +} +function createResourceHandler(event, context) { + if (event.ResourceType.startsWith(SDK_RESOURCE_TYPE_PREFIX)) { + return new SdkHandler(event, context); + } + switch (event.ResourceType) { + case ASSERT_RESOURCE_TYPE: + return new AssertionHandler(event, context); + case RESULTS_RESOURCE_TYPE: + return new ResultsCollectionHandler(event, context); + default: + throw new Error(`Unsupported resource type "${event.ResourceType}`); + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/aws-stepfunctions-tasks-eventbridge-put-events-integ.template.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/aws-stepfunctions-tasks-eventbridge-put-events-integ.template.json index 77e5fcbe59b03..7a6899ba12948 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/aws-stepfunctions-tasks-eventbridge-put-events-integ.template.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/aws-stepfunctions-tasks-eventbridge-put-events-integ.template.json @@ -114,13 +114,6 @@ ] } }, - "Outputs": { - "stateMachineArn": { - "Value": { - "Ref": "StateMachine2E01A3A5" - } - } - }, "Mappings": { "ServiceprincipalMap": { "af-south-1": { @@ -214,5 +207,15 @@ "states": "states.us-west-2.amazonaws.com" } } + }, + "Outputs": { + "ExportsOutputRefStateMachine2E01A3A5BA46F753": { + "Value": { + "Ref": "StateMachine2E01A3A5" + }, + "Export": { + "Name": "aws-stepfunctions-tasks-eventbridge-put-events-integ:ExportsOutputRefStateMachine2E01A3A5BA46F753" + } + } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/cdk.out index 90bef2e09ad39..2efc89439fab8 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"17.0.0"} \ No newline at end of file +{"version":"18.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/integ.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/integ.json index 11fd50aaf44cd..8f6868201ff0d 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/integ.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/integ.json @@ -1,14 +1,11 @@ { "version": "18.0.0", "testCases": { - "aws-stepfunctions-tasks/test/eventbridge/integ.put-events": { + "DefaultTestCase": { "stacks": [ "aws-stepfunctions-tasks-eventbridge-put-events-integ" ], - "diffAssets": false, - "stackUpdateWorkflow": true + "assertionStack": "IntegTestDefaultTestCaseDeployAssert4F885E41" } - }, - "synthContext": {}, - "enableLookups": false + } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/manifest.json index 9000908b6b329..86b2d7285740d 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "17.0.0", + "version": "18.0.0", "artifacts": { "Tree": { "type": "cdk:tree", @@ -39,20 +39,102 @@ "data": "StateMachine2E01A3A5" } ], - "/aws-stepfunctions-tasks-eventbridge-put-events-integ/stateMachineArn": [ + "/aws-stepfunctions-tasks-eventbridge-put-events-integ/Service-principalMap": [ { "type": "aws:cdk:logicalId", - "data": "stateMachineArn" + "data": "ServiceprincipalMap" } ], - "/aws-stepfunctions-tasks-eventbridge-put-events-integ/Service-principalMap": [ + "/aws-stepfunctions-tasks-eventbridge-put-events-integ/Exports/Output{\"Ref\":\"StateMachine2E01A3A5\"}": [ { "type": "aws:cdk:logicalId", - "data": "ServiceprincipalMap" + "data": "ExportsOutputRefStateMachine2E01A3A5BA46F753" } ] }, "displayName": "aws-stepfunctions-tasks-eventbridge-put-events-integ" + }, + "IntegTestDefaultTestCaseDeployAssert4F885E41": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "IntegTestDefaultTestCaseDeployAssert4F885E41.template.json", + "validateOnSynth": false + }, + "dependencies": [ + "aws-stepfunctions-tasks-eventbridge-put-events-integ" + ], + "metadata": { + "/IntegTest/DefaultTestCase/DeployAssert": [ + { + "type": "aws:cdk:asset", + "data": { + "path": "asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle", + "id": "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", + "packaging": "zip", + "sourceHash": "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", + "s3BucketParameter": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB", + "s3KeyParameter": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4", + "artifactHashParameter": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0ArtifactHash4C05AFBC" + } + } + ], + "/IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsstartExecution/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "SdkQueryStepFunctionsstartExecution" + } + ], + "/IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "SdkQueryStepFunctionsdescribeExecution" + } + ], + "/IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/EqualsAssertionStepFunctionsdescribeExecution/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "SdkQueryStepFunctionsdescribeExecutionEqualsAssertionStepFunctionsdescribeExecutionA4C8D5D7" + } + ], + "/IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/EqualsAssertionStepFunctionsdescribeExecution/AssertionResults": [ + { + "type": "aws:cdk:logicalId", + "data": "AssertionResultsEqualsAssertionStepFunctionsdescribeExecution" + } + ], + "/IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73" + } + ], + "/IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F" + } + ], + "/IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3Bucket": [ + { + "type": "aws:cdk:logicalId", + "data": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB" + } + ], + "/IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3VersionKey": [ + { + "type": "aws:cdk:logicalId", + "data": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4" + } + ], + "/IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/ArtifactHash": [ + { + "type": "aws:cdk:logicalId", + "data": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0ArtifactHash4C05AFBC" + } + ] + }, + "displayName": "IntegTest/DefaultTestCase/DeployAssert" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/tree.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/tree.json index 11ad076a1ad87..5e93ec6bff393 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/tree.json @@ -205,14 +205,6 @@ "version": "0.0.0" } }, - "stateMachineArn": { - "id": "stateMachineArn", - "path": "aws-stepfunctions-tasks-eventbridge-put-events-integ/stateMachineArn", - "constructInfo": { - "fqn": "@aws-cdk/core.CfnOutput", - "version": "0.0.0" - } - }, "Service-principalMap": { "id": "Service-principalMap", "path": "aws-stepfunctions-tasks-eventbridge-put-events-integ/Service-principalMap", @@ -220,12 +212,294 @@ "fqn": "@aws-cdk/core.CfnMapping", "version": "0.0.0" } + }, + "Exports": { + "id": "Exports", + "path": "aws-stepfunctions-tasks-eventbridge-put-events-integ/Exports", + "children": { + "Output{\"Ref\":\"StateMachine2E01A3A5\"}": { + "id": "Output{\"Ref\":\"StateMachine2E01A3A5\"}", + "path": "aws-stepfunctions-tasks-eventbridge-put-events-integ/Exports/Output{\"Ref\":\"StateMachine2E01A3A5\"}", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } } }, "constructInfo": { "fqn": "@aws-cdk/core.Stack", "version": "0.0.0" } + }, + "IntegTest": { + "id": "IntegTest", + "path": "IntegTest", + "children": { + "DefaultTestCase": { + "id": "DefaultTestCase", + "path": "IntegTest/DefaultTestCase", + "children": { + "DeployAssert": { + "id": "DeployAssert", + "path": "IntegTest/DefaultTestCase/DeployAssert", + "children": { + "Default": { + "id": "Default", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default", + "children": { + "SdkQueryStepFunctionsstartExecution": { + "id": "SdkQueryStepFunctionsstartExecution", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsstartExecution", + "children": { + "SdkProvider": { + "id": "SdkProvider", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsstartExecution/SdkProvider", + "children": { + "AssertionsProvider": { + "id": "AssertionsProvider", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsstartExecution/SdkProvider/AssertionsProvider", + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AssertionsProvider", + "version": "0.0.0" + } + }, + "Default": { + "id": "Default", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsstartExecution/Default", + "children": { + "Default": { + "id": "Default", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsstartExecution/Default/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.SdkQuery", + "version": "0.0.0" + } + }, + "SdkQueryStepFunctionsdescribeExecution": { + "id": "SdkQueryStepFunctionsdescribeExecution", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution", + "children": { + "SdkProvider": { + "id": "SdkProvider", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/SdkProvider", + "children": { + "AssertionsProvider": { + "id": "AssertionsProvider", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/SdkProvider/AssertionsProvider", + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AssertionsProvider", + "version": "0.0.0" + } + }, + "Default": { + "id": "Default", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/Default", + "children": { + "Default": { + "id": "Default", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/Default/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + }, + "EqualsAssertionStepFunctionsdescribeExecution": { + "id": "EqualsAssertionStepFunctionsdescribeExecution", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/EqualsAssertionStepFunctionsdescribeExecution", + "children": { + "AssertionProvider": { + "id": "AssertionProvider", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/EqualsAssertionStepFunctionsdescribeExecution/AssertionProvider", + "children": { + "AssertionsProvider": { + "id": "AssertionsProvider", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/EqualsAssertionStepFunctionsdescribeExecution/AssertionProvider/AssertionsProvider", + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AssertionsProvider", + "version": "0.0.0" + } + }, + "Default": { + "id": "Default", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/EqualsAssertionStepFunctionsdescribeExecution/Default", + "children": { + "Default": { + "id": "Default", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/EqualsAssertionStepFunctionsdescribeExecution/Default/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + }, + "AssertionResults": { + "id": "AssertionResults", + "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/EqualsAssertionStepFunctionsdescribeExecution/AssertionResults", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.EqualsAssertion", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.SdkQuery", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.DeployAssert", + "version": "0.0.0" + } + }, + "SingletonFunction1488541a7b23466481b69b4408076b81": { + "id": "SingletonFunction1488541a7b23466481b69b4408076b81", + "path": "IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81", + "children": { + "Staging": { + "id": "Staging", + "path": "IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Staging", + "constructInfo": { + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + }, + "Handler": { + "id": "Handler", + "path": "IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + }, + "AssetParameters": { + "id": "AssetParameters", + "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters", + "children": { + "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0": { + "id": "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", + "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", + "children": { + "S3Bucket": { + "id": "S3Bucket", + "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3Bucket", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "S3VersionKey": { + "id": "S3VersionKey", + "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3VersionKey", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "ArtifactHash": { + "id": "ArtifactHash", + "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/ArtifactHash", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } } }, "constructInfo": { diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/integ-tests/test-case.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/integ-tests/test-case.ts index d04ffad502c67..c3de8c8711853 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/lib/integ-tests/test-case.ts +++ b/packages/@aws-cdk/cloud-assembly-schema/lib/integ-tests/test-case.ts @@ -73,6 +73,13 @@ export interface TestCase extends TestOptions { * `exclusively` is passed */ readonly stacks: string[]; + + /** + * The name of the stack that contains assertions + * + * @default - no assertion stack + */ + readonly assertionStack?: string; } /** diff --git a/packages/@aws-cdk/integ-runner/lib/runner/integ-test-runner.ts b/packages/@aws-cdk/integ-runner/lib/runner/integ-test-runner.ts index aa51bd3523e33..705f9f045e6e9 100644 --- a/packages/@aws-cdk/integ-runner/lib/runner/integ-test-runner.ts +++ b/packages/@aws-cdk/integ-runner/lib/runner/integ-test-runner.ts @@ -1,9 +1,10 @@ import * as path from 'path'; import { RequireApproval } from '@aws-cdk/cloud-assembly-schema'; import { DeployOptions, DestroyOptions } from 'cdk-cli-wrapper'; +import * as fs from 'fs-extra'; import * as logger from '../logger'; import { chain, exec } from '../utils'; -import { DestructiveChange } from '../workers/common'; +import { DestructiveChange, AssertionResults, AssertionResult } from '../workers/common'; import { IntegRunnerOptions, IntegRunner, DEFAULT_SYNTH_OPTIONS } from './runner-base'; /** @@ -125,14 +126,15 @@ export class IntegTestRunner extends IntegRunner { * The update workflow exists to check for cases where a change would cause * a failure to an existing stack, but not for a newly created stack. */ - public runIntegTestCase(options: RunOptions): void { + public runIntegTestCase(options: RunOptions): AssertionResults | undefined { + let assertionResults: AssertionResults | undefined; const actualTestCase = this.actualTestSuite.testSuite[options.testCaseName]; const clean = options.clean ?? true; const updateWorkflowEnabled = (options.updateWorkflow ?? true) && (actualTestCase.stackUpdateWorkflow ?? true); try { if (!options.dryRun && (actualTestCase.cdkCommandOptions?.deploy?.enabled ?? true)) { - this.deploy( + assertionResults = this.deploy( { ...this.defaultArgs, profile: this.profile, @@ -152,7 +154,11 @@ export class IntegTestRunner extends IntegRunner { output: this.cdkOutDir, }); } - this.createSnapshot(); + // only create the snapshot if there are no assertion assertion results + // (i.e. no failures) + if (!assertionResults) { + this.createSnapshot(); + } } catch (e) { throw e; } finally { @@ -172,6 +178,7 @@ export class IntegTestRunner extends IntegRunner { } this.cleanup(); } + return assertionResults; } /** @@ -210,7 +217,7 @@ export class IntegTestRunner extends IntegRunner { deployArgs: DeployOptions, updateWorkflowEnabled: boolean, testCaseName: string, - ): void { + ): AssertionResults | undefined { const actualTestCase = this.actualTestSuite.testSuite[testCaseName]; try { if (actualTestCase.hooks?.preDeploy) { @@ -238,26 +245,79 @@ export class IntegTestRunner extends IntegRunner { lookups: this.expectedTestSuite?.enableLookups, }); } + // now deploy the "actual" test. If there are any assertions + // deploy the assertion stack as well this.cdk.deploy({ ...deployArgs, lookups: this.actualTestSuite.enableLookups, - stacks: actualTestCase.stacks, + stacks: [ + ...actualTestCase.stacks, + ...actualTestCase.assertionStack ? [actualTestCase.assertionStack] : [], + ], + rollback: false, output: this.cdkOutDir, ...actualTestCase?.cdkCommandOptions?.deploy?.args, + ...actualTestCase.assertionStack ? { outputsFile: path.join(this.cdkOutDir, 'assertion-results.json') } : undefined, context: this.getContext(actualTestCase?.cdkCommandOptions?.deploy?.args?.context), app: this.hasTmpActualSnapshot() ? this.cdkOutDir : this.cdkApp, }); + if (actualTestCase.hooks?.postDeploy) { exec([chain(actualTestCase.hooks?.postDeploy)], { cwd: path.dirname(this.snapshotDir), }); } + + if (actualTestCase.assertionStack) { + return this.processAssertionResults( + path.join(this.directory, this.cdkOutDir, 'assertion-results.json'), + actualTestCase.assertionStack, + ); + } } catch (e) { this.parseError(e, actualTestCase.cdkCommandOptions?.deploy?.expectError ?? false, actualTestCase.cdkCommandOptions?.deploy?.expectedMessage, ); } + return; + } + + /** + * Process the outputsFile which contains the assertions results as stack + * outputs + */ + private processAssertionResults(file: string, assertionStackId: string): AssertionResults | undefined { + const results: AssertionResults = {}; + if (fs.existsSync(file)) { + try { + const outputs: { [key: string]: { [key: string]: string } } = fs.readJSONSync(file); + + if (assertionStackId in outputs) { + for (const [assertionId, result] of Object.entries(outputs[assertionStackId])) { + if (assertionId.startsWith('AssertionResults')) { + const assertionResult: AssertionResult = JSON.parse(result.replace(/\n/g, '\\n')); + if (assertionResult.status === 'fail') { + results[assertionId] = assertionResult; + } + } + } + } + } catch (e) { + // if there are outputs, but they cannot be processed, then throw an error + // so that the test fails + results[assertionStackId] = { + status: 'fail', + message: `error processing assertion results: ${e}`, + }; + } finally { + // remove the outputs file so it is not part of the snapshot + // it will contain env specific information from values + // resolved at deploy time + fs.unlinkSync(file); + } + } + return Object.keys(results).length > 0 ? results : undefined; } /** @@ -276,4 +336,3 @@ export class IntegTestRunner extends IntegRunner { } } } - diff --git a/packages/@aws-cdk/integ-runner/lib/workers/common.ts b/packages/@aws-cdk/integ-runner/lib/workers/common.ts index 8b9e297e772ec..bcd6a03e36d2b 100644 --- a/packages/@aws-cdk/integ-runner/lib/workers/common.ts +++ b/packages/@aws-cdk/integ-runner/lib/workers/common.ts @@ -1,8 +1,30 @@ +import { format } from 'util'; import { ResourceImpact } from '@aws-cdk/cloudformation-diff'; import * as chalk from 'chalk'; import * as logger from '../logger'; import { IntegTestConfig } from '../runner/integration-tests'; +/** + * The aggregate results from running assertions on a test case + */ +export type AssertionResults = { [id: string]: AssertionResult }; + +/** + * The result of an individual assertion + */ +export interface AssertionResult { + /** + * The assertion message. If the assertion failed, this will + * include the reason. + */ + readonly message: string; + + /** + * Whether the assertion succeeded or failed + */ + readonly status: 'success' | 'fail'; +} + /** * Config for an integration test */ @@ -155,6 +177,11 @@ export enum DiagnosticReason { * The integration test succeeded */ TEST_SUCCESS = 'TEST_SUCCESS', + + /** + * The assertion failed + */ + ASSERTION_FAILED = 'ASSERTION_FAILED', } /** @@ -191,6 +218,16 @@ export function printSummary(total: number, failed: number): void { } } +/** + * Format the assertion results so that the results can be + * printed + */ +export function formatAssertionResults(results: AssertionResults): string { + return Object.entries(results) + .map(([id, result]) => format('%s\n%s', id, result.message)) + .join('\n'); +} + /** * Print out the results from tests */ @@ -210,5 +247,8 @@ export function printResults(diagnostic: Diagnostic): void { break; case DiagnosticReason.TEST_FAILED: logger.error(' %s - Failed! %s\n%s', diagnostic.testName, chalk.gray(`${diagnostic.duration}s`), diagnostic.message); + break; + case DiagnosticReason.ASSERTION_FAILED: + logger.error(' %s - Assertions Failed! %s\n%s', diagnostic.testName, chalk.gray(`${diagnostic.duration}s`), diagnostic.message); } } diff --git a/packages/@aws-cdk/integ-runner/lib/workers/extract/extract_worker.ts b/packages/@aws-cdk/integ-runner/lib/workers/extract/extract_worker.ts index 748e0d1b88448..948329eddee1c 100644 --- a/packages/@aws-cdk/integ-runner/lib/workers/extract/extract_worker.ts +++ b/packages/@aws-cdk/integ-runner/lib/workers/extract/extract_worker.ts @@ -1,7 +1,7 @@ import * as workerpool from 'workerpool'; import { IntegSnapshotRunner, IntegTestRunner } from '../../runner'; import { IntegTestConfig } from '../../runner/integration-tests'; -import { DiagnosticReason, IntegTestWorkerConfig } from '../common'; +import { DiagnosticReason, IntegTestWorkerConfig, formatAssertionResults } from '../common'; import { IntegTestBatchRequest } from '../integ-test-worker'; /** @@ -32,18 +32,28 @@ export function integTestWorker(request: IntegTestBatchRequest): IntegTestWorker } for (const testCaseName of Object.keys(tests)) { try { - runner.runIntegTestCase({ + const results = runner.runIntegTestCase({ testCaseName, clean: request.clean, dryRun: request.dryRun, updateWorkflow: request.updateWorkflow, }); - workerpool.workerEmit({ - reason: DiagnosticReason.TEST_SUCCESS, - testName: testCaseName, - message: 'Success', - duration: (Date.now() - start) / 1000, - }); + if (results) { + failures.push(test); + workerpool.workerEmit({ + reason: DiagnosticReason.ASSERTION_FAILED, + testName: testCaseName, + message: formatAssertionResults(results), + duration: (Date.now() - start) / 1000, + }); + } else { + workerpool.workerEmit({ + reason: DiagnosticReason.TEST_SUCCESS, + testName: testCaseName, + message: 'Success', + duration: (Date.now() - start) / 1000, + }); + } } catch (e) { failures.push(test); workerpool.workerEmit({ diff --git a/packages/@aws-cdk/integ-runner/test/runner/integ-test-runner.test.ts b/packages/@aws-cdk/integ-runner/test/runner/integ-test-runner.test.ts index c75a8b23e247f..78f321e81d9aa 100644 --- a/packages/@aws-cdk/integ-runner/test/runner/integ-test-runner.test.ts +++ b/packages/@aws-cdk/integ-runner/test/runner/integ-test-runner.test.ts @@ -87,6 +87,7 @@ describe('IntegTest runIntegTests', () => { context: expect.any(Object), versionReporting: false, lookups: false, + rollback: false, stacks: ['test-stack', 'new-test-stack'], }); expect(destroyMock).toHaveBeenCalledWith({ @@ -127,6 +128,7 @@ describe('IntegTest runIntegTests', () => { context: expect.not.objectContaining({ [AVAILABILITY_ZONE_FALLBACK_CONTEXT_KEY]: ['test-region-1a', 'test-region-1b', 'test-region-1c'], }), + rollback: false, lookups: false, stacks: ['stack1'], output: 'cdk-integ.out.integ-test1', @@ -170,6 +172,7 @@ describe('IntegTest runIntegTests', () => { }), versionReporting: false, lookups: true, + rollback: false, stacks: ['test-stack'], output: 'cdk-integ.out.test-with-snapshot-assets-diff', profile: undefined, @@ -324,6 +327,7 @@ describe('IntegTest runIntegTests', () => { versionReporting: false, context: expect.any(Object), profile: 'test-profile', + rollback: false, lookups: false, stacks: ['stack1'], output: 'cdk-integ.out.integ-test1', diff --git a/packages/@aws-cdk/integ-tests/README.md b/packages/@aws-cdk/integ-tests/README.md index aaa74d5447641..364041efa421d 100644 --- a/packages/@aws-cdk/integ-tests/README.md +++ b/packages/@aws-cdk/integ-tests/README.md @@ -16,8 +16,52 @@ +## Overview + +This library is meant to be used in combination with the [integ-runner]() CLI +to enable users to write and execute integration tests for AWS CDK Constructs. + +An integration test should be defined as a CDK application, and +there should be a 1:1 relationship between an integration test and a CDK application. + +So for example, in order to create an integration test called `my-function` +we would need to create a file to contain our integration test application. + +*test/integ.my-function.ts* + +```ts +const app = new App(); +const stack = new Stack(); +new lambda.Function(stack, 'MyFunction', { + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + code: lambda.Code.fromAsset(path.join(__dirname, 'lambda-handler')), +}); +``` + +This is a self contained CDK application which we could deploy by running + +```bash +cdk deploy --app 'node test/integ.my-function.js' +``` + +In order to turn this into an integration test, all that is needed is to +use the `IntegTest` construct. + +```ts +declare const app: App; +declare const stack: Stack; +new IntegTest(app, 'Integ', { testCases: [stack] }); +``` + +You will notice that the `stack` is registered to the `IntegTest` as a test case. +Each integration test can contain multiple test cases, which are just instances +of a stack. See the [Usage](#usage) section for more details. + ## Usage +### IntegTest + Suppose you have a simple stack, that only encapsulates a Lambda function with a certain handler: @@ -66,10 +110,8 @@ class StackUnderTest extends Stack { // Beginning of the test suite const app = new App(); -const stack = new Stack(app, 'stack'); - -const differentArchsCase = new IntegTestCase(stack, 'DifferentArchitectures', { - stacks: [ +new IntegTest(app, 'DifferentArchitectures', { + testCases: [ new StackUnderTest(app, 'Stack1', { architecture: lambda.Architecture.ARM_64, }), @@ -78,13 +120,6 @@ const differentArchsCase = new IntegTestCase(stack, 'DifferentArchitectures', { }), ], }); - -// There must be exactly one instance of TestCase per file -new IntegTest(app, 'integ-test', { - - // Register as many test cases as you want here - testCases: [differentArchsCase], -}); ``` This is all the instruction you need for the integration test runner to know @@ -98,8 +133,8 @@ const stackUnderTest = new Stack(app, 'StackUnderTest', /* ... */); const stack = new Stack(app, 'stack'); -const testCase = new IntegTestCase(stack, 'CustomizedDeploymentWorkflow', { - stacks: [stackUnderTest], +const testCase = new IntegTest(app, 'CustomizedDeploymentWorkflow', { + testCases: [stackUnderTest], diffAssets: true, stackUpdateWorkflow: true, cdkCommandOptions: { @@ -116,9 +151,206 @@ const testCase = new IntegTestCase(stack, 'CustomizedDeploymentWorkflow', { }, }, }); +``` + +### IntegTestCaseStack + +In the majority of cases an integration test will contain a single `IntegTestCase`. +By default when you create an `IntegTest` an `IntegTestCase` is created for you +and all of your test cases are registered to this `IntegTestCase`. The `IntegTestCase` +and `IntegTestCaseStack` constructs are only needed when it is necessary to +defined different options for individual test cases. + +For example, you might want to have one test case where `diffAssets` is enabled. + +```ts +declare const app: App; +declare const stackUnderTest: Stack; +const testCaseWithAssets = new IntegTestCaseStack(app, 'TestCaseAssets', { + diffAssets: true, +}); + +new IntegTest(app, 'Integ', { testCases: [stackUnderTest, testCaseWithAssets] }); +``` + +## Assertions + +This library also provides a utility to make assertions against the infrastructure that the integration test deploys. + +The easiest way to do this is to create a `TestCase` and then access the `DeployAssert` that is automatically created. + +```ts +declare const app: App; +declare const stack: Stack; + +const integ = new IntegTest(app, 'Integ', { testCases: [stack] }); +integ.assert.awsApiCall('S3', 'getObject'); +``` + +### DeployAssert + +Assertions are created by using the `DeployAssert` construct. This construct creates it's own `Stack` separate from +any stacks that you create as part of your integration tests. This `Stack` is treated differently from other stacks +by the `integ-runner` tool. For example, this stack will not be diffed by the `integ-runner`. + +Any assertions that you create should be created in the scope of `DeployAssert`. For example, + +```ts +declare const app: App; + +const assert = new DeployAssert(app); +new SdkQuery(assert, 'GetObject', { + service: 'S3', + api: 'getObject', +}); +``` + +`DeployAssert` also provides utilities to register your own assertions. + +```ts +declare const myCustomResource: CustomResource; +declare const app: App; +const assert = new DeployAssert(app); +assert.strictEquals( + 'CustomAssertion', + ExpectedResult.fromObject({ foo: 'bar' }), + ActualResult.fromCustomResource(myCustomResource, 'data'), +); +``` + +In the above example an assertion is created that will trigger a user defined `CustomResource` +and assert that the `data` attribute is equal to `{ foo: 'bar' }`. + +### SdkQuery + +A common method to retrieve the "actual" results to compare with what is expected is to make an +AWS API call to receive some data. This library does this by utilizing CloudFormation custom resources +which means that CloudFormation will call out to a Lambda Function which will +use the AWS JavaScript SDK to make the API call. + +This can be done by using the class directory: + +```ts +declare const assert: DeployAssert; + +new SdkQuery(assert, 'MyAssertion', { + service: 'SQS', + api: 'receiveMessage', + parameters: { + QueueUrl: 'url', + }, +}); +``` + +Or by using the `awsApiCall` method on `DeployAssert`: + +```ts +declare const app: App; +const assert = new DeployAssert(app); +assert.awsApiCall('SQS', 'receiveMessage', { + QueueUrl: 'url', +}); +``` + +### EqualsAssertion + +This library currently provides the ability to assert that two values are equal +to one another by utilizing the `EqualsAssertion` class. This utilizes a Lambda +backed `CustomResource` which in tern uses the [Match](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.assertions.Match.html) utility from the +[@aws-cdk/assertions](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.assertions-readme.html) library. + + +```ts +declare const app: App; +declare const stack: Stack; +declare const queue: sqs.Queue; +declare const fn: lambda.IFunction; + +const integ = new IntegTest(app, 'Integ', { + testCases: [stack], +}); + +integ.assert.invokeFunction({ + functionName: fn.functionName, + invocationType: InvocationType.EVENT, + payload: JSON.stringify({ status: 'OK' }), +}); + +const message = integ.assert.awsApiCall('SQS', 'receiveMessage', { + QueueUrl: queue.queueUrl, + WaitTimeSeconds: 20, +}); + +new EqualsAssertion(integ.assert, 'ReceiveMessage', { + actual: ActualResult.fromSdkQuery(message, 'Messages.0.Body'), + expected: ExpectedResult.fromObject({ + requestContext: { + condition: 'Success', + }, + requestPayload: { + status: 'OK', + }, + responseContext: { + statusCode: 200, + }, + responsePayload: 'success', + }), + assertionType: AssertionType.OBJECT_LIKE, +}); +``` + +### Examples + +#### Invoke a Lambda Function + +In this example there is a Lambda Function that is invoked and +we assert that the payload that is returned is equal to '200'. + +```ts +declare const lambdaFunction: lambda.IFunction; +declare const app: App; + +const stack = new Stack(app, 'cdk-integ-lambda-bundling'); + +const integ = new IntegTest(app, 'IntegTest', { + testCases: [stack], +}); + +const invoke = integ.assert.invokeFunction({ + functionName: lambdaFunction.functionName, +}); +invoke.assertObjectLike({ + Payload: '200', +}); +``` + +#### Make an AWS API Call + +In this example there is a StepFunctions state machine that is executed +and then we assert that the result of the execution is successful. + +```ts +declare const app: App; +declare const stack: Stack; +declare const sm: IStateMachine; + +const testCase = new IntegTest(app, 'IntegTest', { + testCases: [stack], +}); + +// Start an execution +const start = testCase.assert.awsApiCall('StepFunctions', 'startExecution', { + stateMachineArn: sm.stateMachineArn, +}); + +// describe the results of the execution +const describe = testCase.assert.awsApiCall('StepFunctions', 'describeExecution', { + executionArn: start.getAttString('executionArn'), +}); -new IntegTest(app, 'integ-test', { - testCases: [testCase], +// assert the results +describe.assertObjectLike({ + status: 'SUCCEEDED', }); ``` diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/assertions.ts b/packages/@aws-cdk/integ-tests/lib/assertions/assertions.ts index 1b3ba0f14f7bf..c18586a65ce59 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/assertions.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/assertions.ts @@ -1,37 +1,43 @@ -import { CustomResource } from '@aws-cdk/core'; +import { CustomResource, CfnOutput } from '@aws-cdk/core'; import { Construct } from 'constructs'; -import { IAssertion } from './deploy-assert'; +import { ExpectedResult, ActualResult } from './common'; import { AssertionRequest, AssertionsProvider, ASSERT_RESOURCE_TYPE, AssertionType } from './providers'; -// + // keep this import separate from other imports to reduce chance for merge conflicts with v2-main // eslint-disable-next-line no-duplicate-imports, import/order import { Construct as CoreConstruct } from '@aws-cdk/core'; + /** * Options for an EqualsAssertion */ export interface EqualsAssertionProps { /** - * The CustomResource that continains the "actual" results + * The actual results to compare */ - readonly inputResource: CustomResource; + readonly actual: ActualResult; /** - * The CustomResource attribute that continains the "actual" results + * The expected result to assert */ - readonly inputResourceAtt: string; + readonly expected: ExpectedResult; /** - * The expected result to assert + * The type of assertion to make + * + * @default - AssertionType.DEFAULT */ - readonly expected: any; + readonly assertionType?: AssertionType; } /** * Construct that creates a CustomResource to assert that two * values are equal */ -export class EqualsAssertion extends CoreConstruct implements IAssertion { +export class EqualsAssertion extends CoreConstruct { + /** + * The result of the assertion + */ public readonly result: string; constructor(scope: Construct, id: string, props: EqualsAssertionProps) { @@ -39,15 +45,22 @@ export class EqualsAssertion extends CoreConstruct implements IAssertion { const assertionProvider = new AssertionsProvider(this, 'AssertionProvider'); const properties: AssertionRequest = { - actual: props.inputResource.getAttString(props.inputResourceAtt), - expected: props.expected, - assertionType: AssertionType.EQUALS, + actual: props.actual.result, + expected: props.expected.result, + assertionType: props.assertionType ?? AssertionType.EQUALS, }; const resource = new CustomResource(this, 'Default', { serviceToken: assertionProvider.serviceToken, - properties, + properties: { + ...properties, + salt: Date.now().toString(), // always update, + }, resourceType: ASSERT_RESOURCE_TYPE, }); this.result = resource.getAttString('data'); + + new CfnOutput(this, 'AssertionResults', { + value: this.result, + }).overrideLogicalId(`AssertionResults${id}`); } } diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/common.ts b/packages/@aws-cdk/integ-tests/lib/assertions/common.ts new file mode 100644 index 0000000000000..2fa2fa2535630 --- /dev/null +++ b/packages/@aws-cdk/integ-tests/lib/assertions/common.ts @@ -0,0 +1,58 @@ +import { CustomResource } from '@aws-cdk/core'; +import { SdkQuery } from './sdk'; +/** + * Represents the "actual" results to compare + */ +export abstract class ActualResult { + /** + * Get the actual results from a CustomResource + */ + public static fromCustomResource(customResource: CustomResource, attribute: string): ActualResult { + return { + result: customResource.getAttString(attribute), + }; + } + + /** + * Get the actual results from a SdkQuery + */ + public static fromSdkQuery(query: SdkQuery, attribute: string): ActualResult { + return { + result: query.getAttString(attribute), + }; + } + + /** + * The actual results as a string + */ + public abstract result: string; +} + +/** + * Represents the "expected" results to compare + */ +export abstract class ExpectedResult { + /** + * Expected result is a string + */ + public static fromString(expected: string): ExpectedResult { + return { + result: expected, + }; + } + + /** + * Expected result is an object. The object will + * be stringified before being sent to the customResource + */ + public static fromObject(expected: { [key: string]: any }): ExpectedResult { + return { + result: JSON.stringify(expected), + }; + } + + /** + * The expected results as a string + */ + public abstract result: string; +} diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts b/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts index 8ef74b5ce56a5..7e68fa5294885 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts @@ -1,24 +1,20 @@ -import { CfnOutput, CustomResource, Lazy } from '@aws-cdk/core'; +import { Stack } from '@aws-cdk/core'; import { Construct, IConstruct, Node } from 'constructs'; +import { EqualsAssertion } from './assertions'; +import { ExpectedResult, ActualResult } from './common'; import { md5hash } from './private/hash'; -import { RESULTS_RESOURCE_TYPE, AssertionsProvider } from './providers'; -import { SdkQuery, SdkQueryOptions } from './sdk'; +import { AssertionType } from './providers'; +import { SdkQuery, LambdaInvokeFunction, LambdaInvokeFunctionProps } from './sdk'; const DEPLOY_ASSERT_SYMBOL = Symbol.for('@aws-cdk/integ-tests.DeployAssert'); // keep this import separate from other imports to reduce chance for merge conflicts with v2-main // eslint-disable-next-line no-duplicate-imports, import/order -import { Construct as CoreConstruct } from '@aws-cdk/core'; -/** - * Represents a deploy time assertion - */ -export interface IAssertion { - /** - * The result of the assertion - */ - readonly result: string; -} + +// keep this import separate from other imports to reduce chance for merge conflicts with v2-main +// eslint-disable-next-line no-duplicate-imports, import/order +import { Construct as CoreConstruct } from '@aws-cdk/core'; /** * Options for DeployAssert @@ -42,7 +38,7 @@ export class DeployAssert extends CoreConstruct { * Finds a DeployAssert construct in the given scope */ public static of(construct: IConstruct): DeployAssert { - const scopes = Node.of(construct).scopes.reverse(); + const scopes = Node.of(Node.of(construct).root).findAll(); const deployAssert = scopes.find(s => DeployAssert.isDeployAssert(s)); if (!deployAssert) { throw new Error('No DeployAssert construct found in scopes'); @@ -50,46 +46,76 @@ export class DeployAssert extends CoreConstruct { return deployAssert as DeployAssert; } - /** @internal */ - public readonly _assertions: IAssertion[]; - constructor(scope: Construct) { - super(scope, 'DeployAssert'); + scope = new Stack(scope, 'DeployAssert'); + super(scope, 'Default'); Object.defineProperty(this, DEPLOY_ASSERT_SYMBOL, { value: true }); - this._assertions = []; - - const provider = new AssertionsProvider(this, 'ResultsProvider'); + } - const resource = new CustomResource(this, 'ResultsCollection', { - serviceToken: provider.serviceToken, - properties: { - assertionResults: Lazy.list({ - produce: () => this._assertions.map(a => a.result), - }), - }, - resourceType: RESULTS_RESOURCE_TYPE, + /** + * Query AWS using JavaScript SDK V2 API calls. This can be used to either + * trigger an action or to return a result that can then be asserted against + * an expected value + * + * @example + * declare const app: App; + * const assert = new DeployAssert(app); + * assert.awsApiCall('SQS', 'sendMessage', { + * QueueUrl: 'url', + * MessageBody: 'hello', + * }); + * const message = assert.awsApiCall('SQS', 'receiveMessage', { + * QueueUrl: 'url', + * }); + * message.assertObjectLike({ + * Messages: [{ Body: 'hello' }], + * }); + */ + public awsApiCall(service: string, api: string, parameters?: any): SdkQuery { + return new SdkQuery(this, `SdkQuery${service}${api}`, { + api, + service, + parameters, }); + } - // TODO: need to show/store this information - new CfnOutput(this, 'Results', { - value: `\n${resource.getAttString('message')}`, - }).overrideLogicalId('Results'); + /** + * Assert that two values are strictly equal + */ + public strictEquals(id: string, expected: ExpectedResult, actual: ActualResult): void { + new EqualsAssertion(this, `EqualsAssertion${id}`, { + actual, + expected, + }); } /** - * Query AWS using JavaScript SDK V2 API calls + * Invoke a lambda function and return the response which can be asserted + * + * @example + * declare const app: App; + * const assert = new DeployAssert(app); + * const invoke = assert.invokeFunction({ + * functionName: 'my-function', + * }); + * invoke.assertObjectLike({ + * Payload: '200', + * }); */ - public queryAws(options: SdkQueryOptions): SdkQuery { - const id = md5hash(options); - return new SdkQuery(this, `SdkQuery${id}`, options); + public invokeFunction(props: LambdaInvokeFunctionProps): LambdaInvokeFunction { + const hash = md5hash(Stack.of(this).resolve(props)); + return new LambdaInvokeFunction(this, `LambdaInvoke${hash}`, props); } /** - * Register an assertion that should be run as part of the - * deployment + * Assert that an object is a subset of another */ - public registerAssertion(assertion: IAssertion) { - this._assertions.push(assertion); + public objectLike(id: string, expected: { [key: string]: any }, actual: ActualResult): void { + new EqualsAssertion(this, `EqualsAssertion${id}`, { + actual, + expected: ExpectedResult.fromObject(expected), + assertionType: AssertionType.OBJECT_LIKE, + }); } } diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/index.ts b/packages/@aws-cdk/integ-tests/lib/assertions/index.ts index f1f833d9f78a4..c430b38482222 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/index.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/index.ts @@ -2,3 +2,4 @@ export * from './assertions'; export * from './sdk'; export * from './deploy-assert'; export * from './providers'; +export * from './common'; diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts index 8efd972d5f98e..77749ac8dfed6 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts @@ -1,34 +1,65 @@ /* eslint-disable no-console */ -import * as assert from 'assert'; import { CustomResourceHandler } from './base'; -import { AssertionRequest, AssertionResult } from './types'; +import { AssertionResult, AssertionRequest } from './types'; + +// this is needed for esbuild to work correctly +// eslint-disable-next-line @typescript-eslint/no-require-imports,import/no-extraneous-dependencies +const { Match } = require('@aws-cdk/assertions/lib/match'); export class AssertionHandler extends CustomResourceHandler { protected async processEvent(request: AssertionRequest): Promise { + let actual = request.actual; + const expected = decodeCall(request.expected); let result: AssertionResult; + let matcher; + console.log(`Testing equality between ${JSON.stringify(request.actual)} and ${JSON.stringify(request.expected)}`); + switch (request.assertionType) { - case 'equals': - console.log(`Testing equality between ${JSON.stringify(request.actual)} and ${JSON.stringify(request.expected)}`); - try { - assert.deepStrictEqual(request.actual, request.expected); - result = { data: { status: 'pass' } }; - } catch (e) { - if (e instanceof assert.AssertionError) { - result = { - data: { - status: 'fail', - message: e.message, - }, - }; - } else { - throw e; - } + case 'objectLike': + if (typeof actual === 'string') { + actual = JSON.parse(actual); } + matcher = Match.objectLike(expected); + break; + case 'equals': + matcher = Match.exact(expected); + break; + case 'arrayWith': + matcher = Match.arrayWith(expected); break; default: throw new Error(`Unsupported query type ${request.assertionType}`); } + const matchResult = matcher.test(actual); + matchResult.finished(); + if (matchResult.hasFailed()) { + result = { + data: JSON.stringify({ + status: 'fail', + message: [ + ...matchResult.toHumanStrings(), + JSON.stringify(matchResult.target, undefined, 2), + ].join('\n'), + }), + }; + } else { + result = { + data: JSON.stringify({ + status: 'pass', + }), + }; + } + return result; } } + +function decodeCall(call: string | undefined) { + if (!call) { return undefined; } + try { + return JSON.parse(call); + } catch (e) { + return call; + } +} diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/sdk.ts b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/sdk.ts index fed1174d3fb27..a216b756e0374 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/sdk.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/sdk.ts @@ -1,6 +1,7 @@ /* eslint-disable no-console */ import { CustomResourceHandler } from './base'; import { SdkRequest, SdkResult } from './types'; +import { decode } from './utils'; /** * Flattens a nested object @@ -44,16 +45,3 @@ export class SdkHandler extends CustomResourceHandler) { - return JSON.parse(JSON.stringify(object), (_k, v) => { - switch (v) { - case 'TRUE:BOOLEAN': - return true; - case 'FALSE:BOOLEAN': - return false; - default: - return v; - } - }); -} diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/types.ts b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/types.ts index f0ff05507ae61..c6052dd2579bd 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/types.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/types.ts @@ -63,6 +63,18 @@ export enum AssertionType { * Assert that two values are equal */ EQUALS = 'equals', + + /** + * The keys and their values must be present in the target but the target + * can be a superset. + */ + OBJECT_LIKE = 'objectLike', + + /** + * Matches the specified pattern with the array + * The set of elements must be in the same order as would be found + */ + ARRAY_WITH = 'arrayWith', } /** @@ -94,7 +106,7 @@ export interface AssertionResult { /** * The result of an assertion */ - readonly data: AssertionResultData; + readonly data: string; } /** diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/utils.ts b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/utils.ts new file mode 100644 index 0000000000000..12e4ec65ff8e3 --- /dev/null +++ b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/utils.ts @@ -0,0 +1,13 @@ + +export function decode(object: Record): any { + return JSON.parse(JSON.stringify(object), (_k, v) => { + switch (v) { + case 'TRUE:BOOLEAN': + return true; + case 'FALSE:BOOLEAN': + return false; + default: + return v; + } + }); +} diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/providers/provider.ts b/packages/@aws-cdk/integ-tests/lib/assertions/providers/provider.ts index 155996452713c..0b416158cc717 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/providers/provider.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/providers/provider.ts @@ -1,7 +1,5 @@ import * as path from 'path'; -import * as iam from '@aws-cdk/aws-iam'; -import * as lambda from '@aws-cdk/aws-lambda'; -import { Duration } from '@aws-cdk/core'; +import { Duration, CfnResource, AssetStaging, Stack, FileAssetPackaging, Token, Lazy, Reference } from '@aws-cdk/core'; import { Construct } from 'constructs'; // keep this import separate from other imports to reduce chance for merge conflicts with v2-main @@ -9,6 +7,153 @@ import { Construct } from 'constructs'; import { Construct as CoreConstruct } from '@aws-cdk/core'; let SDK_METADATA: any = undefined; + +/** + * integ-tests can only depend on '@aws-cdk/core' so + * this construct creates a lambda function provider using + * only CfnResource + */ +class LambdaFunctionProvider extends CoreConstruct { + /** + * The ARN of the lambda function which can be used + * as a serviceToken to a CustomResource + */ + public readonly serviceToken: string; + + /** + * A Reference to the provider lambda exeuction role ARN + */ + public readonly roleArn: Reference; + + private readonly policies: any[] = []; + + constructor(scope: Construct, id: string/*, props?: LambdaFunctionProviderProps*/) { + super(scope, id); + + const staging = new AssetStaging(this, 'Staging', { + sourcePath: path.join(__dirname, 'lambda-handler.bundle'), + }); + + const stack = Stack.of(this); + const asset = stack.synthesizer.addFileAsset({ + fileName: staging.relativeStagedPath(stack), + sourceHash: staging.assetHash, + packaging: FileAssetPackaging.ZIP_DIRECTORY, + }); + + const role = new CfnResource(this, 'Role', { + type: 'AWS::IAM::Role', + properties: { + AssumeRolePolicyDocument: { + Version: '2012-10-17', + Statement: [{ Action: 'sts:AssumeRole', Effect: 'Allow', Principal: { Service: 'lambda.amazonaws.com' } }], + }, + ManagedPolicyArns: [ + { 'Fn::Sub': 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole' }, + ], + Policies: [ + { + PolicyName: 'Inline', + PolicyDocument: { + Version: '2012-10-17', + Statement: Lazy.list({ produce: () => this.policies }), + }, + }, + ], + }, + }); + + const handler = new CfnResource(this, 'Handler', { + type: 'AWS::Lambda::Function', + properties: { + Runtime: 'nodejs14.x', + Code: { + S3Bucket: asset.bucketName, + S3Key: asset.objectKey, + }, + Timeout: Duration.minutes(2).toSeconds(), + Handler: 'index.handler', + Role: role.getAtt('Arn'), + }, + }); + + this.serviceToken = Token.asString(handler.getAtt('Arn')); + this.roleArn = role.getAtt('Arn'); + } + + public addPolicies(policies: any[]): void { + this.policies.push(...policies); + } + +} + +interface SingletonFunctionProps { + /** + * A unique identifier to identify this lambda + * + * The identifier should be unique across all custom resource providers. + * We recommend generating a UUID per provider. + */ + readonly uuid: string; + + /** + * A list of IAM policies to add to the lambdaFunction + * execution role + */ + readonly policies: any[]; +} + +/** + * Mimic the singletonfunction construct in '@aws-cdk/aws-lambda' + */ +class SingletonFunction extends CoreConstruct { + public readonly serviceToken: string; + + public readonly lambdaFunction: LambdaFunctionProvider; + private readonly policies: any[] = []; + constructor(scope: Construct, id: string, props: SingletonFunctionProps) { + super(scope, id); + this.lambdaFunction = this.ensureFunction(props); + this.serviceToken = this.lambdaFunction.serviceToken; + } + + /** + * The policies can be added by different constructs + */ + onPrepare(): void { + this.lambdaFunction.addPolicies(this.policies); + } + + private ensureFunction(props: SingletonFunctionProps): LambdaFunctionProvider { + const constructName = 'SingletonFunction' + slugify(props.uuid); + const existing = Stack.of(this).node.tryFindChild(constructName); + if (existing) { + return existing as LambdaFunctionProvider; + } + + return new LambdaFunctionProvider(Stack.of(this), constructName); + } + + /** + * Create a policy statement from a specific api call + */ + public addPolicyStatementFromSdkCall(service: string, api: string, resources?: string[]): void { + if (SDK_METADATA === undefined) { + // eslint-disable-next-line + SDK_METADATA = require('./sdk-api-metadata.json'); + } + const srv = service.toLowerCase(); + const iamService = (SDK_METADATA[srv] && SDK_METADATA[srv].prefix) || srv; + const iamAction = api.charAt(0).toUpperCase() + api.slice(1); + this.policies.push({ + Action: [`${iamService}:${iamAction}`], + Effect: 'Allow', + Resource: resources || ['*'], + }); + } + +} + /** * Represents an assertions provider. The creates a singletone * Lambda Function that will create a single function per stack @@ -16,24 +161,39 @@ let SDK_METADATA: any = undefined; * assertion providers */ export class AssertionsProvider extends CoreConstruct { + /** + * The ARN of the lambda function which can be used + * as a serviceToken to a CustomResource + */ public readonly serviceToken: string; - private readonly grantPrincipal: iam.IPrincipal; + /** + * A reference to the provider Lambda Function + * execution Role ARN + */ + public readonly handlerRoleArn: Reference; + + private readonly policies: any[] = []; + private readonly handler: SingletonFunction; constructor(scope: Construct, id: string) { super(scope, id); - const handler = new lambda.SingletonFunction(this, 'AssertionsProvider', { - code: lambda.Code.fromAsset(path.join(__dirname, 'lambda-handler')), - runtime: lambda.Runtime.NODEJS_14_X, - handler: 'index.handler', + this.handler = new SingletonFunction(this, 'AssertionsProvider', { uuid: '1488541a-7b23-4664-81b6-9b4408076b81', - timeout: Duration.minutes(2), + policies: Lazy.list({ produce: () => this.policies }), }); - this.grantPrincipal = handler.grantPrincipal; - this.serviceToken = handler.functionArn; + this.handlerRoleArn = this.handler.lambdaFunction.roleArn; + + this.serviceToken = this.handler.serviceToken; } + /** + * Encode an object so it can be passed + * as custom resource parameters. Custom resources will convert + * all input parameters to strings so we encode non-strings here + * so we can then decode them correctly in the provider function + */ public encode(obj: any): any { if (!obj) { return obj; @@ -50,19 +210,14 @@ export class AssertionsProvider extends CoreConstruct { }); } - public addPolicyStatementFromSdkCall(service: string, api: string, resources?: string[]): iam.PolicyStatement { - if (SDK_METADATA === undefined) { - // eslint-disable-next-line - SDK_METADATA = require('./sdk-api-metadata.json'); - } - const srv = service.toLowerCase(); - const iamService = (SDK_METADATA[srv] && SDK_METADATA[srv].prefix) || srv; - const iamAction = api.charAt(0).toUpperCase() + api.slice(1); - const statement = new iam.PolicyStatement({ - actions: [`${iamService}:${iamAction}`], - resources: resources || ['*'], - }); - this.grantPrincipal.addToPolicy(statement); - return statement; + /** + * Create a policy statement from a specific api call + */ + public addPolicyStatementFromSdkCall(service: string, api: string, resources?: string[]): void { + this.handler.addPolicyStatementFromSdkCall(service, api, resources); } } + +function slugify(x: string): string { + return x.replace(/[^a-zA-Z0-9]/g, ''); +} diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts b/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts index ead56af7732d9..0913dbd94599a 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts @@ -1,9 +1,8 @@ -import { CustomResource, Reference, Lazy } from '@aws-cdk/core'; +import { CustomResource, Reference, Lazy, CfnResource, Stack, ArnFormat } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { EqualsAssertion } from './assertions'; -import { IAssertion } from './deploy-assert'; -import { md5hash } from './private/hash'; -import { AssertionsProvider, SDK_RESOURCE_TYPE_PREFIX } from './providers'; +import { ExpectedResult, ActualResult } from './common'; +import { AssertionsProvider, SDK_RESOURCE_TYPE_PREFIX, AssertionType } from './providers'; // keep this import separate from other imports to reduce chance for merge conflicts with v2-main // eslint-disable-next-line no-duplicate-imports, import/order @@ -25,6 +24,8 @@ export interface SdkQueryOptions { /** * Any parameters to pass to the api call + * + * @default - no parameters */ readonly parameters?: any; } @@ -34,29 +35,38 @@ export interface SdkQueryOptions { */ export interface SdkQueryProps extends SdkQueryOptions {} +/** + * Construct that creates a custom resource that will perform + * a query using the AWS SDK + */ export class SdkQuery extends CoreConstruct { private readonly sdkCallResource: CustomResource; private flattenResponse: string = 'false'; + private readonly name: string; + + protected provider: AssertionsProvider; constructor(scope: Construct, id: string, props: SdkQueryProps) { super(scope, id); - const provider = new AssertionsProvider(this, 'SdkProvider'); - provider.addPolicyStatementFromSdkCall(props.service, props.api); + this.provider = new AssertionsProvider(this, 'SdkProvider'); + this.provider.addPolicyStatementFromSdkCall(props.service, props.api); + this.name = `${props.service}${props.api}`; this.sdkCallResource = new CustomResource(this, 'Default', { - serviceToken: provider.serviceToken, + serviceToken: this.provider.serviceToken, properties: { service: props.service, api: props.api, - parameters: provider.encode(props.parameters), + parameters: this.provider.encode(props.parameters), flattenResponse: Lazy.string({ produce: () => this.flattenResponse }), + salt: Date.now().toString(), }, - resourceType: `${SDK_RESOURCE_TYPE_PREFIX}${props.service}${props.api}`, + resourceType: `${SDK_RESOURCE_TYPE_PREFIX}${this.name}`, }); // Needed so that all the policies set up by the provider should be available before the custom resource is provisioned. - this.sdkCallResource.node.addDependency(provider); + this.sdkCallResource.node.addDependency(this.provider); } /** @@ -87,20 +97,154 @@ export class SdkQuery extends CoreConstruct { } /** - * Creates an assertion custom resource that will assert that the response - * from the SDKQuery equals the 'expected' value - */ - public assertEqual(expected: any, actualAttr?: string): IAssertion { - const hash = md5hash(expected); - let inputResourceAtt = 'apiCallResponse'; - if (actualAttr) { - this.flattenResponse = 'true'; - inputResourceAtt = `apiCallResponse.${actualAttr}`; - } - return new EqualsAssertion(this, `AssertEquals${hash}`, { - expected, - inputResource: this.sdkCallResource, - inputResourceAtt, + * Asserts that the expected value is strictly equal to + * the result of the SdkQuery. Missing fields will result in + * a failure. + */ + public assertObjectEqual(expected: { [key: string]: any }): void { + new EqualsAssertion(this, `AssertEquals${this.name}`, { + expected: ExpectedResult.fromObject(expected), + actual: ActualResult.fromCustomResource(this.sdkCallResource, 'apiCallResponse'), }); } + + /** + * Asserts that the expected value is a subset of + * the result of the SdkQuery + */ + public assertObjectLike(expected: { [key: string]: any }): void { + new EqualsAssertion(this, `EqualsAssertion${this.name}`, { + actual: ActualResult.fromCustomResource(this.sdkCallResource, 'apiCallResponse'), + expected: ExpectedResult.fromObject(expected), + assertionType: AssertionType.OBJECT_LIKE, + }); + } + + /** + * Asserts that the expected value is equal to the + * results of the SdkQuery. The result of the SdkQuery + * must be a string value. + */ + public assertStringEqual(expected: string): void { + new EqualsAssertion(this, `EqualsAssertion${this.name}`, { + actual: ActualResult.fromCustomResource(this.sdkCallResource, 'apiCallResponse'), + expected: ExpectedResult.fromString(expected), + assertionType: AssertionType.EQUALS, + }); + } +} + +/** + * Set to Tail to include the execution log in the response. + * Applies to synchronously invoked functions only. + */ +export enum LogType { + /** + * The log messages are not returned in the response + */ + NONE = 'None', + + /** + * The log messages are returned in the response + */ + TAIL = 'Tail', } + +/** + * The type of invocation. Default is REQUEST_RESPONE + */ +export enum InvocationType { + /** + * Invoke the function asynchronously. + * Send events that fail multiple times to the function's + * dead-letter queue (if it's configured). + * The API response only includes a status code. + */ + EVENT = 'Event', + + /** + * Invoke the function synchronously. + * Keep the connection open until the function returns a response or times out. + * The API response includes the function response and additional data. + */ + REQUEST_RESPONE = 'RequestResponse', + + /** + * Validate parameter values and verify that the user + * or role has permission to invoke the function. + */ + DRY_RUN = 'DryRun', +} + +/** + * Options to pass to the Lambda invokeFunction API call + */ +export interface LambdaInvokeFunctionProps { + /** + * The name of the function to invoke + */ + readonly functionName: string; + + /** + * The type of invocation to use + * + * @default InvocationType.REQUEST_RESPONE + */ + readonly invocationType?: InvocationType; + + /** + * Whether to return the logs as part of the response + * + * @default LogType.NONE + */ + readonly logType?: LogType; + + /** + * Payload to send as part of the invoke + * + * @default - no payload + */ + readonly payload?: string; +} + +/** + * An AWS Lambda Invoke function API call. + * Use this istead of the generic SdkQuery in order to + * invoke a lambda function. This will automatically create + * the correct permissions to invoke the function + */ +export class LambdaInvokeFunction extends SdkQuery { + constructor(scope: Construct, id: string, props: LambdaInvokeFunctionProps) { + super(scope, id, { + api: 'invoke', + service: 'Lambda', + parameters: { + FunctionName: props.functionName, + InvocationType: props.invocationType, + LogType: props.logType, + Payload: props.payload, + }, + }); + + const stack = Stack.of(this); + // need to give the assertion lambda permission to invoke + new CfnResource(this, 'Invoke', { + type: 'AWS::Lambda::Permission', + properties: { + Action: 'lambda:InvokeFunction', + FunctionName: props.functionName, + Principal: this.provider.handlerRoleArn, + }, + }); + + // the api call is 'invoke', but the permission is 'invokeFunction' + // so need to handle it specially + this.provider.addPolicyStatementFromSdkCall('Lambda', 'invokeFunction', [stack.formatArn({ + service: 'lambda', + resource: 'function', + arnFormat: ArnFormat.COLON_RESOURCE_NAME, + resourceName: props.functionName, + })]); + } +} + diff --git a/packages/@aws-cdk/integ-tests/lib/index.ts b/packages/@aws-cdk/integ-tests/lib/index.ts index 638d20a4d1d1a..bacf0560f3cbf 100644 --- a/packages/@aws-cdk/integ-tests/lib/index.ts +++ b/packages/@aws-cdk/integ-tests/lib/index.ts @@ -1 +1,2 @@ export * from './test-case'; +export * from './assertions'; diff --git a/packages/@aws-cdk/integ-tests/lib/test-case.ts b/packages/@aws-cdk/integ-tests/lib/test-case.ts index 2c0c6582a413c..0937fc6672930 100644 --- a/packages/@aws-cdk/integ-tests/lib/test-case.ts +++ b/packages/@aws-cdk/integ-tests/lib/test-case.ts @@ -1,10 +1,14 @@ import { IntegManifest, Manifest, TestCase, TestOptions } from '@aws-cdk/cloud-assembly-schema'; -import { attachCustomSynthesis, Stack, ISynthesisSession } from '@aws-cdk/core'; +import { attachCustomSynthesis, Stack, ISynthesisSession, StackProps } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { DeployAssert } from './assertions'; import { IntegManifestSynthesizer } from './manifest-synthesizer'; +const TEST_CASE_STACK_SYMBOL = Symbol.for('@aws-cdk/integ-tests.IntegTestCaseStack'); + // keep this import separate from other imports to reduce chance for merge conflicts with v2-main // eslint-disable-next-line no-duplicate-imports, import/order -import { Construct } from '@aws-cdk/core'; +import { Construct as CoreConstruct } from '@aws-cdk/core'; /** * Properties of an integration test case @@ -19,10 +23,20 @@ export interface IntegTestCaseProps extends TestOptions { /** * An integration test case. Allows the definition of test properties that * apply to all stacks under this case. + * + * It is recommended that you use the IntegTest construct since that will create + * a default IntegTestCase */ -export class IntegTestCase extends Construct { +export class IntegTestCase extends CoreConstruct { + /** + * Make assertions on resources in this test case + */ + public readonly assert: DeployAssert; + constructor(scope: Construct, private readonly id: string, private readonly props: IntegTestCaseProps) { super(scope, id); + + this.assert = new DeployAssert(this); } /** @@ -32,43 +46,115 @@ export class IntegTestCase extends Construct { get manifest(): IntegManifest { return { version: Manifest.version(), - testCases: { [this.id]: toTestCase(this.props) }, + testCases: { [this.id]: this.toTestCase(this.props) }, + }; + } + + private toTestCase(props: IntegTestCaseProps): TestCase { + return { + ...props, + assertionStack: Stack.of(this.assert).artifactId, + stacks: props.stacks.map(s => s.artifactId), }; } } +/** + * Properties of an integration test case stack + */ +export interface IntegTestCaseStackProps extends TestOptions, StackProps {} + +/** + * An integration test case stack. Allows the definition of test properties + * that should apply to this stack. + * + * This should be used if there are multiple stacks in the integration test + * and it is necessary to specify different test case option for each. Otherwise + * normal stacks should be added to IntegTest + */ +export class IntegTestCaseStack extends Stack { + /** + * Returns whether the construct is a IntegTestCaseStack + */ + public static isIntegTestCaseStack(x: any): x is IntegTestCaseStack { + return x !== null && typeof(x) === 'object' && TEST_CASE_STACK_SYMBOL in x; + } + + /** + * Make assertions on resources in this test case + */ + public readonly assert: DeployAssert; + + /** + * The underlying IntegTestCase that is created + * @internal + */ + public readonly _testCase: IntegTestCase; + + constructor(scope: Construct, id: string, props?: IntegTestCaseStackProps) { + super(scope, id, props); + + Object.defineProperty(this, TEST_CASE_STACK_SYMBOL, { value: true }); + + // TODO: should we only have a single DeployAssert per test? + this.assert = new DeployAssert(this); + this._testCase = new IntegTestCase(this, `${id}TestCase`, { + ...props, + stacks: [this], + }); + } + +} + /** * Integration test properties */ -export interface IntegTestProps { +export interface IntegTestProps extends TestOptions { /** * List of test cases that make up this test */ - readonly testCases: IntegTestCase[]; + readonly testCases: Stack[]; } /** * A collection of test cases. Each test case file should contain exactly one * instance of this class. */ -export class IntegTest extends Construct { - constructor(scope: Construct, id: string, private readonly props: IntegTestProps) { +export class IntegTest extends CoreConstruct { + /** + * Make assertions on resources in this test case + */ + public readonly assert: DeployAssert; + private readonly testCases: IntegTestCase[]; + constructor(scope: Construct, id: string, props: IntegTestProps) { super(scope, id); + + const defaultTestCase = new IntegTestCase(this, 'DefaultTestCase', { + stacks: props.testCases.filter(stack => !IntegTestCaseStack.isIntegTestCaseStack(stack)), + hooks: props.hooks, + regions: props.regions, + diffAssets: props.diffAssets, + allowDestroy: props.allowDestroy, + cdkCommandOptions: props.cdkCommandOptions, + stackUpdateWorkflow: props.stackUpdateWorkflow, + }); + this.assert = defaultTestCase.assert; + + this.testCases = [ + defaultTestCase, + ...props.testCases + .filter(stack => IntegTestCaseStack.isIntegTestCaseStack(stack)) + .map(stack => (stack as IntegTestCaseStack)._testCase), + ]; } + protected onPrepare(): void { attachCustomSynthesis(this, { onSynthesize: (session: ISynthesisSession) => { - const synthesizer = new IntegManifestSynthesizer(this.props.testCases); + const synthesizer = new IntegManifestSynthesizer(this.testCases); synthesizer.synthesize(session); }, }); } } - -function toTestCase(props: IntegTestCaseProps): TestCase { - return { - ...props, - stacks: props.stacks.map(s => s.artifactId), - }; -} diff --git a/packages/@aws-cdk/integ-tests/package.json b/packages/@aws-cdk/integ-tests/package.json index 83d5b4f5af4f8..6c63e6938d428 100644 --- a/packages/@aws-cdk/integ-tests/package.json +++ b/packages/@aws-cdk/integ-tests/package.json @@ -40,6 +40,7 @@ }, "scripts": { "build": "cdk-build", + "bundle": "esbuild --bundle lib/assertions/providers/lambda-handler/index.ts --target=node14 --platform=node --external:aws-sdk --outfile=lib/assertions/providers/lambda-handler.bundle/index.js", "lint": "cdk-lint", "package": "cdk-package", "awslint": "cdk-awslint", @@ -61,6 +62,7 @@ "license": "Apache-2.0", "devDependencies": { "@aws-cdk/cdk-build-tools": "0.0.0", + "@aws-cdk/cx-api": "0.0.0", "@aws-cdk/assertions": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/fs-extra": "^8.1.2", @@ -75,22 +77,12 @@ "dependencies": { "@aws-cdk/cloud-assembly-schema": "0.0.0", "@aws-cdk/core": "0.0.0", - "@aws-cdk/cx-api": "0.0.0", - "@aws-cdk/aws-lambda": "0.0.0", - "@aws-cdk/triggers": "0.0.0", - "@aws-cdk/aws-iam": "0.0.0", - "@aws-cdk/custom-resources": "0.0.0", "constructs": "^3.3.69" }, "peerDependencies": { "@aws-cdk/cloud-assembly-schema": "0.0.0", - "@aws-cdk/assertions": "0.0.0", "@aws-cdk/core": "0.0.0", - "@aws-cdk/triggers": "0.0.0", - "@aws-cdk/custom-resources": "0.0.0", - "constructs": "^3.3.69", - "@aws-cdk/aws-lambda": "0.0.0", - "@aws-cdk/aws-iam": "0.0.0" + "constructs": "^3.3.69" }, "repository": { "url": "https://github.com/aws/aws-cdk.git", @@ -105,6 +97,19 @@ "engines": { "node": ">= 10.13.0 <13 || >=13.7.0" }, + "cdk-build": { + "pre": [ + "yarn bundle" + ], + "env": { + "AWSLINT_BASE_CONSTRUCT": true + } + }, + "awslint": { + "exclude": [ + "construct-ctor:@aws-cdk/integ-tests.DeployAssert." + ] + }, "nozem": { "ostools": [ "unzip", diff --git a/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture b/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture index 648e54426b3e4..0f49ebf717693 100644 --- a/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture @@ -1,12 +1,26 @@ import * as lambda from '@aws-cdk/aws-lambda'; -import { IntegTestCase, IntegTest } from '@aws-cdk/integ-tests'; +import { + IntegTestCase, + IntegTest, + IntegTestCaseStack, + DeployAssert, + SdkQuery, + EqualsAssertion, + ActualResult, + ExpectedResult, + InvocationType, + AssertionType, +} from '@aws-cdk/integ-tests'; import { App, Construct, Stack, StackProps, + CustomResource, } from '@aws-cdk/core'; import * as path from 'path'; +import * as sqs from '@aws-cdk/aws-sqs'; +import { IStateMachine } from '@aws-cdk/aws-stepfunctions'; import { RequireApproval } from '@aws-cdk/cloud-assembly-schema'; /// here diff --git a/packages/@aws-cdk/integ-tests/test/assertions/assertions.test.ts b/packages/@aws-cdk/integ-tests/test/assertions/assertions.test.ts deleted file mode 100644 index c8558c6460b0a..0000000000000 --- a/packages/@aws-cdk/integ-tests/test/assertions/assertions.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Template } from '@aws-cdk/assertions'; -import { App, CustomResource, Stack } from '@aws-cdk/core'; -import { IAssertion, DeployAssert, EqualsAssertion } from '../../lib/assertions'; - -describe('Assertion', () => { - test('registration', () => { - const app = new App(); - const stack = new Stack(app); - const deployAssert = new DeployAssert(stack); - - class MyAssertion implements IAssertion { - public result = 'result'; - } - const assertion = new MyAssertion(); - deployAssert.registerAssertion(assertion); - - expect(deployAssert._assertions).toContain(assertion); - }); -}); - -describe('EqualsAssertion', () => { - test('default', () => { - const app = new App(); - const stack = new Stack(app); - const deployAssert = new DeployAssert(stack); - const customRes = new CustomResource(stack, 'MyCustomResource', { - serviceToken: 'serviceToken', - }); - deployAssert.registerAssertion(new EqualsAssertion(stack, 'MyAssertion', { - expected: { foo: 'bar' }, - inputResource: customRes, - inputResourceAtt: 'foo', - })); - - Template.fromStack(stack).hasResourceProperties('Custom::DeployAssert@AssertEquals', { - actual: { - 'Fn::GetAtt': [ - 'MyCustomResource', - 'foo', - ], - }, - expected: { - foo: 'bar', - }, - assertionType: 'equals', - }); - }); -}); diff --git a/packages/@aws-cdk/integ-tests/test/assertions/deploy-assert.test.ts b/packages/@aws-cdk/integ-tests/test/assertions/deploy-assert.test.ts index bb73e87b2da7e..dec5ebafccd0f 100644 --- a/packages/@aws-cdk/integ-tests/test/assertions/deploy-assert.test.ts +++ b/packages/@aws-cdk/integ-tests/test/assertions/deploy-assert.test.ts @@ -1,96 +1,146 @@ import { Template } from '@aws-cdk/assertions'; -// import * as iam from '@aws-cdk/aws-iam'; import { App, Stack } from '@aws-cdk/core'; -import { IAssertion, DeployAssert } from '../../lib/assertions'; +import { DeployAssert, LogType, InvocationType, ExpectedResult, ActualResult } from '../../lib/assertions'; describe('DeployAssert', () => { - describe('ResultsCollection', () => { + + test('of', () => { + const app = new App(); + const stack = new Stack(app); + new DeployAssert(app); + expect(() => { + DeployAssert.of(stack); + }).not.toThrow(); + }); + + test('throws if no DeployAssert', () => { + const app = new App(); + const stack = new Stack(app); + expect(() => { + DeployAssert.of(stack); + }).toThrow(/No DeployAssert construct found in scopes/); + }); + + test('isDeployAssert', () => { + const app = new App(); + const deployAssert = new DeployAssert(app); + const isDeployAssert = DeployAssert.isDeployAssert(deployAssert); + expect(isDeployAssert).toEqual(true); + }); + + describe('invokeFunction', () => { test('default', () => { // GIVEN const app = new App(); - const stack = new Stack(app, 'MyStack'); + const deployAssert = new DeployAssert(app); // WHEN - new DeployAssert(stack); - + deployAssert.invokeFunction({ + functionName: 'my-func', + logType: LogType.TAIL, + payload: JSON.stringify({ key: 'val' }), + invocationType: InvocationType.EVENT, + }); // THEN - const template = Template.fromStack(stack); - template.resourceCountIs('Custom::DeployAssert@ResultsCollection', 1); - - template.hasOutput('Results', {}); + const template = Template.fromStack(Stack.of(deployAssert)); + template.hasResourceProperties('Custom::DeployAssert@SdkCallLambdainvoke', { + service: 'Lambda', + api: 'invoke', + parameters: { + FunctionName: 'my-func', + InvocationType: 'Event', + LogType: 'Tail', + Payload: '{"key":"val"}', + }, + }); }); + }); - test('assertion results are part of the output', () => { + describe('assertions', () => { + test('strictEquals', () => { // GIVEN - class MyAssertion implements IAssertion { - public readonly result: string; - constructor(result: string) { - this.result = result; - } - } - const app = new App(); - const stack = new Stack(app, 'MyStack'); + const deplossert = new DeployAssert(app); + const query = deplossert.awsApiCall('MyService', 'MyApi'); // WHEN - const deployAssert = new DeployAssert(stack); - deployAssert.registerAssertion( - new MyAssertion('MyAssertion1Result'), - ); - deployAssert.registerAssertion( - new MyAssertion('MyAssertion2Result'), + deplossert.strictEquals( + 'MyAssertion', + ExpectedResult.fromString('foo'), + ActualResult.fromSdkQuery(query, 'att'), ); + // THEN + const template = Template.fromStack(Stack.of(deplossert)); + template.hasResourceProperties('Custom::DeployAssert@AssertEquals', { + expected: 'foo', + assertionType: 'equals', + actual: { + 'Fn::GetAtt': [ + 'SdkQueryMyServiceMyApi', + 'apiCallResponse.att', + ], + }, + }); + }); + + test('objectLike', () => { + // GIVEN + const app = new App(); + const deplossert = new DeployAssert(app); + const query = deplossert.awsApiCall('MyService', 'MyApi'); + + // WHEN + deplossert.objectLike( + 'MyAssertion', + { foo: 'bar' }, + ActualResult.fromSdkQuery(query, 'att'), + ); // THEN - const template = Template.fromStack(stack); - template.hasResourceProperties('Custom::DeployAssert@ResultsCollection', { - assertionResults: ['MyAssertion1Result', 'MyAssertion2Result'], + const template = Template.fromStack(Stack.of(deplossert)); + template.hasResourceProperties('Custom::DeployAssert@AssertEquals', { + expected: JSON.stringify({ foo: 'bar' }), + actual: { + 'Fn::GetAtt': [ + 'SdkQueryMyServiceMyApi', + 'apiCallResponse.att', + ], + }, }); }); }); - describe('queryAws', () => { + describe('awsApiCall', () => { test('default', () => { // GIVEN const app = new App(); - const stack = new Stack(app); + const deplossert = new DeployAssert(app); // WHEN - const deplossert = new DeployAssert(stack); - deplossert.queryAws({ - service: 'MyService', - api: 'MyApi', - }); + deplossert.awsApiCall('MyService', 'MyApi'); // THEN - Template.fromStack(stack).hasResourceProperties('Custom::DeployAssert@SdkCallMyServiceMyApi', { + Template.fromStack(Stack.of(deplossert)).hasResourceProperties('Custom::DeployAssert@SdkCallMyServiceMyApi', { api: 'MyApi', service: 'MyService', }); }); - test('multiple queries can be configured', () => { + test('multiple calls can be configured', () => { // GIVEN const app = new App(); - const stack = new Stack(app); // WHEN - const deplossert = new DeployAssert(stack); - deplossert.queryAws({ - service: 'MyService', - api: 'MyApi1', - }); - deplossert.queryAws({ - service: 'MyService', - api: 'MyApi2', - }); + const deplossert = new DeployAssert(app); + deplossert.awsApiCall('MyService', 'MyApi1'); + deplossert.awsApiCall('MyService', 'MyApi2'); // THEN - const template = Template.fromStack(stack); + const template = Template.fromStack(Stack.of(deplossert)); template.resourceCountIs('AWS::Lambda::Function', 1); template.resourceCountIs('Custom::DeployAssert@SdkCallMyServiceMyApi1', 1); template.resourceCountIs('Custom::DeployAssert@SdkCallMyServiceMyApi2', 1); diff --git a/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/assertion.test.ts b/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/assertion.test.ts index 911876c84bdfb..4b977e8d535c7 100644 --- a/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/assertion.test.ts +++ b/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/assertion.test.ts @@ -18,6 +18,56 @@ afterAll(() => { }); describe('AssertionHandler', () => { + describe('objectLike', () => { + test('pass', async () => { + // GIVEN + const handler = assertionHandler() as any; + const request: AssertionRequest = { + assertionType: AssertionType.OBJECT_LIKE, + actual: { + stringParam: 'foo', + numberParam: 3, + booleanParam: true, + }, + expected: JSON.stringify({ + stringParam: 'foo', + }), + }; + + // WHEN + const response: AssertionResult = await handler.processEvent(request); + + // THEN + expect(response.data).toEqual('{"status":"pass"}'); + }); + + test('fail', async () => { + // GIVEN + const handler = assertionHandler() as any; + const request: AssertionRequest = { + assertionType: AssertionType.OBJECT_LIKE, + actual: { + stringParam: 'foo', + numberParam: 3, + booleanParam: true, + }, + expected: JSON.stringify({ + stringParam: 'bar', + }), + }; + + // WHEN + const response: AssertionResult = await handler.processEvent(request); + + // THEN + expect(JSON.parse(response.data)).toEqual({ + status: 'fail', + message: 'Expected bar but received foo at /stringParam (using objectLike matcher)\n' + + '{\n \"stringParam\": \"foo\",\n \"numberParam\": 3,\n \"booleanParam\": true\n}', + }); + }); + }); + describe('equals', () => { test('pass', async () => { // GIVEN @@ -29,18 +79,34 @@ describe('AssertionHandler', () => { numberParam: 3, booleanParam: true, }, - expected: { + expected: JSON.stringify({ stringParam: 'foo', numberParam: 3, booleanParam: true, - }, + }), }; // WHEN const response: AssertionResult = await handler.processEvent(request); // THEN - expect(response.data.status).toEqual('pass'); + expect(response.data).toEqual('{"status":"pass"}'); + }); + + test('string equals pass', async () => { + // GIVEN + const handler = assertionHandler() as any; + const request: AssertionRequest = { + assertionType: AssertionType.EQUALS, + actual: 'foo', + expected: 'foo', + }; + + // WHEN + const response: AssertionResult = await handler.processEvent(request); + + // THEN + expect(response.data).toEqual('{"status":"pass"}'); }); test('fail', async () => { @@ -51,16 +117,19 @@ describe('AssertionHandler', () => { actual: { stringParam: 'foo', }, - expected: { + expected: JSON.stringify({ stringParam: 'bar', - }, + }), }; // WHEN const response: AssertionResult = await handler.processEvent(request); // THEN - expect(response.data.status).toEqual('fail'); + expect(JSON.parse(response.data)).toEqual({ + status: 'fail', + message: 'Expected bar but received foo at /stringParam (using exact matcher)\n{\n \"stringParam\": \"foo\"\n}', + }); }); }); @@ -71,7 +140,7 @@ describe('AssertionHandler', () => { const request: AssertionRequest = { assertionType, actual: 'foo', - expected: 'bar', + expected: JSON.stringify({ foo: 'bar' }), }; // THEN diff --git a/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/results.test.ts b/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/results.test.ts deleted file mode 100644 index 33b0cef42677d..0000000000000 --- a/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/results.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. -import { ResultsCollectionRequest, ResultsCollectionResult } from '../../../../lib/assertions'; -import { ResultsCollectionHandler } from '../../../../lib/assertions/providers/lambda-handler/results'; - -function handler() { - const context: any = { - getRemainingTimeInMillis: () => 50000, - }; - return new ResultsCollectionHandler({} as any, context); // as any to ignore all type checks -} -beforeAll(() => { - jest.useFakeTimers(); - jest.spyOn(process.stderr, 'write').mockImplementation(() => { return true; }); - jest.spyOn(process.stdout, 'write').mockImplementation(() => { return true; }); -}); -afterAll(() => { - jest.useRealTimers(); - jest.restoreAllMocks(); -}); - -describe('ResultsCollectionHandler', () => { - test('default', async () => { - // GIVEN - const resultsCollection = handler() as any; - const request: ResultsCollectionRequest = { - assertionResults: [ - { status: 'pass' }, - { status: 'fail', message: 'something failed' }, - ], - }; - - // WHEN - const result: ResultsCollectionResult = await resultsCollection.processEvent(request); - const split = result.message.split('\n'); - - // THEN - expect(split.length).toEqual(2); - expect(split[0]).toEqual('Test0: pass'); - expect(split[1]).toEqual('Test1: fail - something failed'); - }); - - test('message not displayed for pass', async () => { - // GIVEN - const resultsCollection = handler() as any; - const request: ResultsCollectionRequest = { - assertionResults: [ - { status: 'pass', message: 'OK' }, - ], - }; - - // WHEN - const result: ResultsCollectionResult = await resultsCollection.processEvent(request); - const split = result.message.split('\n'); - - // THEN - expect(split.length).toEqual(1); - expect(split[0]).toEqual('Test0: pass'); - }); -}); diff --git a/packages/@aws-cdk/integ-tests/test/assertions/providers/provider.test.ts b/packages/@aws-cdk/integ-tests/test/assertions/providers/provider.test.ts index 376be437ddb8a..f27d61189e72f 100644 --- a/packages/@aws-cdk/integ-tests/test/assertions/providers/provider.test.ts +++ b/packages/@aws-cdk/integ-tests/test/assertions/providers/provider.test.ts @@ -11,7 +11,7 @@ describe('AssertionProvider', () => { const provider = new AssertionsProvider(stack, 'AssertionProvider'); // THEN - expect(stack.resolve(provider.serviceToken)).toEqual({ 'Fn::GetAtt': ['SingletonLambda1488541a7b23466481b69b4408076b81488C0898', 'Arn'] }); + expect(stack.resolve(provider.serviceToken)).toEqual({ 'Fn::GetAtt': ['SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F', 'Arn'] }); Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', { Handler: 'index.handler', Timeout: 120, @@ -28,19 +28,88 @@ describe('AssertionProvider', () => { provider.addPolicyStatementFromSdkCall('MyService', 'myApi'); // THEN - Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { - PolicyDocument: { - Statement: [ - { - Action: 'myservice:MyApi', - Effect: 'Allow', - Resource: '*', + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + Policies: [ + { + PolicyName: 'Inline', + PolicyDocument: { + Statement: [ + { + Action: ['myservice:MyApi'], + Resource: ['*'], + Effect: 'Allow', + }, + ], }, - ], - }, - Roles: [{ - Ref: 'SingletonLambda1488541a7b23466481b69b4408076b81ServiceRole4E21F0DA', - }], + }, + ], + }); + }); + + test('multiple calls', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + const provider = new AssertionsProvider(stack, 'AssertionsProvider'); + provider.addPolicyStatementFromSdkCall('MyService', 'myApi'); + provider.addPolicyStatementFromSdkCall('MyService2', 'myApi2'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + Policies: [ + { + PolicyName: 'Inline', + PolicyDocument: { + Statement: [ + { + Action: ['myservice:MyApi'], + Resource: ['*'], + Effect: 'Allow', + }, + { + Action: ['myservice2:MyApi2'], + Resource: ['*'], + Effect: 'Allow', + }, + ], + }, + }, + ], + }); + }); + + test('multiple providers, 1 resource', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + const provider = new AssertionsProvider(stack, 'AssertionsProvider'); + const provider2 = new AssertionsProvider(stack, 'AssertionsProvider2'); + provider.addPolicyStatementFromSdkCall('MyService', 'myApi'); + provider2.addPolicyStatementFromSdkCall('MyService2', 'myApi2'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + Policies: [ + { + PolicyName: 'Inline', + PolicyDocument: { + Statement: [ + { + Action: ['myservice:MyApi'], + Resource: ['*'], + Effect: 'Allow', + }, + { + Action: ['myservice2:MyApi2'], + Resource: ['*'], + Effect: 'Allow', + }, + ], + }, + }, + ], }); }); @@ -53,16 +122,21 @@ describe('AssertionProvider', () => { provider.addPolicyStatementFromSdkCall('applicationautoscaling', 'myApi'); // THEN - Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { - PolicyDocument: { - Statement: [ - { - Action: 'application-autoscaling:MyApi', - Effect: 'Allow', - Resource: '*', + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + Policies: [ + { + PolicyName: 'Inline', + PolicyDocument: { + Statement: [ + { + Action: ['application-autoscaling:MyApi'], + Effect: 'Allow', + Resource: ['*'], + }, + ], }, - ], - }, + }, + ], }); }); }); diff --git a/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts b/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts index 2b54beb326e2d..742c725d881ca 100644 --- a/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts +++ b/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts @@ -1,13 +1,12 @@ import { Template, Match } from '@aws-cdk/assertions'; -import { App, Stack } from '@aws-cdk/core'; -import { DeployAssert, SdkQuery } from '../../lib/assertions'; +import { App, Stack, CfnOutput } from '@aws-cdk/core'; +import { DeployAssert, SdkQuery, LambdaInvokeFunction, LogType, InvocationType } from '../../lib/assertions'; describe('SdkQuery', () => { test('default', () => { // GIVEN const app = new App(); - const stack = new Stack(app); - const deplossert = new DeployAssert(stack); + const deplossert = new DeployAssert(app); // WHEN new SdkQuery(deplossert, 'SdkQuery', { @@ -15,9 +14,8 @@ describe('SdkQuery', () => { api: 'MyApi', }); - // THEN - const template = Template.fromStack(stack); + const template = Template.fromStack(Stack.of(deplossert)); template.resourceCountIs('AWS::Lambda::Function', 1); template.hasResourceProperties('Custom::DeployAssert@SdkCallMyServiceMyApi', { service: 'MyService', @@ -29,8 +27,7 @@ describe('SdkQuery', () => { test('parameters', () => { // GIVEN const app = new App(); - const stack = new Stack(app); - const deplossert = new DeployAssert(stack); + const deplossert = new DeployAssert(app); // WHEN new SdkQuery(deplossert, 'SdkQuery', { @@ -42,9 +39,8 @@ describe('SdkQuery', () => { }, }); - // THEN - const template = Template.fromStack(stack); + const template = Template.fromStack(Stack.of(deplossert)); template.resourceCountIs('AWS::Lambda::Function', 1); template.hasResourceProperties('Custom::DeployAssert@SdkCallMyServiceMyApi', { service: 'MyService', @@ -56,28 +52,94 @@ describe('SdkQuery', () => { }); }); - describe('assertEqual', () => { - test('default', () => { + describe('get attribute', () => { + test('getAttString', () => { + // GIVEN + const app = new App(); + const deplossert = new DeployAssert(app); + + // WHEN + const query = new SdkQuery(deplossert, 'SdkQuery', { + service: 'MyService', + api: 'MyApi', + }); + + new CfnOutput(deplossert, 'GetAttString', { + value: query.getAttString('att'), + }).overrideLogicalId('GetAtt'); + + // THEN + const template = Template.fromStack(Stack.of(deplossert)); + template.hasOutput('GetAtt', { + Value: { + 'Fn::GetAtt': [ + 'SdkQuery', + 'apiCallResponse.att', + ], + }, + }); + template.resourceCountIs('AWS::Lambda::Function', 1); + template.hasResourceProperties('Custom::DeployAssert@SdkCallMyServiceMyApi', { + service: 'MyService', + api: 'MyApi', + flattenResponse: 'true', + }); + }); + test('getAtt', () => { // GIVEN const app = new App(); - const stack = new Stack(app); - const deplossert = new DeployAssert(stack); + const deplossert = new DeployAssert(app); // WHEN const query = new SdkQuery(deplossert, 'SdkQuery', { service: 'MyService', api: 'MyApi', }); - query.assertEqual({ foo: 'bar' }); + new CfnOutput(deplossert, 'GetAttString', { + value: query.getAtt('att').toString(), + }).overrideLogicalId('GetAtt'); + + // THEN + const template = Template.fromStack(Stack.of(deplossert)); + template.hasOutput('GetAtt', { + Value: { + 'Fn::GetAtt': [ + 'SdkQuery', + 'apiCallResponse.att', + ], + }, + }); + template.resourceCountIs('AWS::Lambda::Function', 1); + template.hasResourceProperties('Custom::DeployAssert@SdkCallMyServiceMyApi', { + service: 'MyService', + api: 'MyApi', + flattenResponse: 'true', + }); + }); + + }); + + describe('assertEqual', () => { + test('objectEqual', () => { + // GIVEN + const app = new App(); + const deplossert = new DeployAssert(app); + + // WHEN + const query = new SdkQuery(deplossert, 'SdkQuery', { + service: 'MyService', + api: 'MyApi', + }); + query.assertObjectEqual({ foo: 'bar' }); // THEN - const template = Template.fromStack(stack); + const template = Template.fromStack(Stack.of(deplossert)); template.hasResourceProperties('Custom::DeployAssert@AssertEquals', { - expected: { foo: 'bar' }, + expected: JSON.stringify({ foo: 'bar' }), actual: { 'Fn::GetAtt': [ - 'DeployAssertSdkQuery94650089', + 'SdkQuery', 'apiCallResponse', ], }, @@ -85,24 +147,159 @@ describe('SdkQuery', () => { }); }); - test('multiple asserts to the same query', () => { + test('objectLike', () => { // GIVEN const app = new App(); - const stack = new Stack(app); - const deplossert = new DeployAssert(stack); + const deplossert = new DeployAssert(app); // WHEN const query = new SdkQuery(deplossert, 'SdkQuery', { service: 'MyService', api: 'MyApi', }); - query.assertEqual({ foo: 'bar' }); - query.assertEqual({ baz: 'zoo' }); + query.assertObjectLike({ foo: 'bar' }); + // THEN + const template = Template.fromStack(Stack.of(deplossert)); + template.hasResourceProperties('Custom::DeployAssert@AssertEquals', { + expected: JSON.stringify({ foo: 'bar' }), + actual: { + 'Fn::GetAtt': [ + 'SdkQuery', + 'apiCallResponse', + ], + }, + assertionType: 'objectLike', + }); + }); + + test('string', () => { + // GIVEN + const app = new App(); + const deplossert = new DeployAssert(app); + + // WHEN + const query = new SdkQuery(deplossert, 'SdkQuery', { + service: 'MyService', + api: 'MyApi', + }); + query.assertStringEqual('bar'); // THEN - const template = Template.fromStack(stack); - template.resourceCountIs('Custom::DeployAssert@AssertEquals', 2); + const template = Template.fromStack(Stack.of(deplossert)); + template.hasResourceProperties('Custom::DeployAssert@AssertEquals', { + expected: 'bar', + actual: { + 'Fn::GetAtt': [ + 'SdkQuery', + 'apiCallResponse', + ], + }, + assertionType: 'equals', + }); + }); + }); + + describe('invoke lambda', () => { + test('default', () => { + // GIVEN + const app = new App(); + const deplossert = new DeployAssert(app); + + new LambdaInvokeFunction(deplossert, 'Invoke', { + functionName: 'my-func', + logType: LogType.TAIL, + payload: JSON.stringify({ key: 'val' }), + invocationType: InvocationType.EVENT, + }); + + const template = Template.fromStack(Stack.of(deplossert)); + template.hasResourceProperties('Custom::DeployAssert@SdkCallLambdainvoke', { + service: 'Lambda', + api: 'invoke', + parameters: { + FunctionName: 'my-func', + InvocationType: 'Event', + LogType: 'Tail', + Payload: '{"key":"val"}', + }, + }); + template.hasResourceProperties('AWS::Lambda::Permission', { + Action: 'lambda:InvokeFunction', + FunctionName: 'my-func', + Principal: { + 'Fn::GetAtt': [ + 'SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73', + 'Arn', + ], + }, + }); + template.hasResourceProperties('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Version: '2012-10-17', + Statement: [ + { + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + Service: 'lambda.amazonaws.com', + }, + }, + ], + }, + ManagedPolicyArns: [ + { + 'Fn::Sub': 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole', + }, + ], + Policies: [ + { + PolicyName: 'Inline', + PolicyDocument: { + Version: '2012-10-17', + Statement: [ + { + Action: [ + 'lambda:Invoke', + ], + Effect: 'Allow', + Resource: [ + '*', + ], + }, + { + Action: [ + 'lambda:InvokeFunction', + ], + Effect: 'Allow', + Resource: [ + { + 'Fn::Join': [ + '', + [ + 'arn:', + { + Ref: 'AWS::Partition', + }, + ':lambda:', + { + Ref: 'AWS::Region', + }, + ':', + { + Ref: 'AWS::AccountId', + }, + ':function:my-func', + ], + ], + }, + ], + }, + ], + }, + }, + ], + }); }); }); }); diff --git a/packages/@aws-cdk/integ-tests/test/manifest-synthesizer.test.ts b/packages/@aws-cdk/integ-tests/test/manifest-synthesizer.test.ts index b033faf2be0c6..3863da2fceea3 100644 --- a/packages/@aws-cdk/integ-tests/test/manifest-synthesizer.test.ts +++ b/packages/@aws-cdk/integ-tests/test/manifest-synthesizer.test.ts @@ -4,18 +4,27 @@ import * as path from 'path'; import { Manifest } from '@aws-cdk/cloud-assembly-schema'; import { App, Stack } from '@aws-cdk/core'; import { CloudAssemblyBuilder } from '@aws-cdk/cx-api'; -import { IntegTestCase } from '../lib'; +import { IntegTestCase, IntegTest, IntegTestCaseStack } from '../lib'; import { IntegManifestSynthesizer } from '../lib/manifest-synthesizer'; import { IntegManifestWriter } from '../lib/manifest-writer'; +let write: jest.SpyInstance; +let tmpDir: string; +let assembly: CloudAssemblyBuilder; -describe(IntegManifestSynthesizer, () => { - it('synthesizes a multiple manifests', () => { - const write = jest.spyOn(IntegManifestWriter, 'write'); +beforeEach(() => { + write = jest.spyOn(IntegManifestWriter, 'write'); + tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cdk-test')); + assembly = new CloudAssemblyBuilder(tmpDir); +}); + +afterEach(() => { + jest.restoreAllMocks(); +}); +describe(IntegManifestSynthesizer, () => { + it('synthesizes multiple test cases', () => { const app = new App(); const stack = new Stack(app, 'stack'); - const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cdk-test')); - const assembly = new CloudAssemblyBuilder(tmpDir); const synthesizer = new IntegManifestSynthesizer([ new IntegTestCase(stack, 'case1', { @@ -35,12 +44,70 @@ describe(IntegManifestSynthesizer, () => { version: Manifest.version(), testCases: { case1: { + assertionStack: expect.stringMatching(/DeployAssert/), stacks: ['stack-under-test-1'], }, case2: { + assertionStack: expect.stringMatching(/DeployAssert/), stacks: ['stack-under-test-2'], }, }, }, tmpDir); }); + + test('default', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'stack'); + + // WHEN + new IntegTest(app, 'Integ', { + testCases: [stack], + }); + const integAssembly = app.synth(); + const integManifest = Manifest.loadIntegManifest(path.join(integAssembly.directory, 'integ.json')); + + // THEN + expect(integManifest).toEqual({ + version: Manifest.version(), + testCases: { + DefaultTestCase: { + assertionStack: expect.stringMatching(/DeployAssert/), + stacks: ['stack'], + }, + }, + }); + }); + + test('with IntegTestCaseStack', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'stack'); + const testCase = new IntegTestCaseStack(app, 'Case', { + diffAssets: true, + }); + + // WHEN + new IntegTest(app, 'Integ', { + testCases: [stack, testCase], + }); + const integAssembly = app.synth(); + const integManifest = Manifest.loadIntegManifest(path.join(integAssembly.directory, 'integ.json')); + + // THEN + expect(integManifest).toEqual({ + version: Manifest.version(), + testCases: { + DefaultTestCase: { + assertionStack: expect.stringMatching(/DeployAssert/), + stacks: ['stack'], + }, + CaseTestCase: { + assertionStack: expect.stringMatching(/DeployAssert/), + diffAssets: true, + stacks: ['Case'], + }, + }, + }); + }); }); From e1283184d8f6b20c0a3a7b3375fd6f649f34bd45 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Tue, 3 May 2022 11:48:28 +0000 Subject: [PATCH 02/10] updating schema version --- .../cloud-assembly-schema/schema/cloud-assembly.version.json | 2 +- .../@aws-cdk/cloud-assembly-schema/schema/integ.schema.json | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json index 2efc89439fab8..ccdfc1ff96a9d 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json @@ -1 +1 @@ -{"version":"18.0.0"} \ No newline at end of file +{"version":"19.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/integ.schema.json b/packages/@aws-cdk/cloud-assembly-schema/schema/integ.schema.json index 94306087e1147..f18827244648c 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/integ.schema.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/integ.schema.json @@ -45,6 +45,10 @@ "type": "string" } }, + "assertionStack": { + "description": "The name of the stack that contains assertions (Default - no assertion stack)", + "type": "string" + }, "stackUpdateWorkflow": { "description": "Run update workflow on this test case\nThis should only be set to false to test scenarios\nthat are not possible to test as part of the update workflow (Default true)", "type": "boolean" From aa53dfdb3a1a3a0d74fabcb0f9b7501a796af03c Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Tue, 3 May 2022 14:47:40 +0000 Subject: [PATCH 03/10] exporting Match from @aws-cdk/assertions --- packages/@aws-cdk/assertions/lib/helpers-internal/index.ts | 2 ++ packages/@aws-cdk/assertions/package.json | 5 +++++ .../lib/assertions/providers/lambda-handler/assertion.ts | 6 ++---- packages/aws-cdk-lib/package.json | 3 +++ 4 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 packages/@aws-cdk/assertions/lib/helpers-internal/index.ts diff --git a/packages/@aws-cdk/assertions/lib/helpers-internal/index.ts b/packages/@aws-cdk/assertions/lib/helpers-internal/index.ts new file mode 100644 index 0000000000000..7fbde68f0a98c --- /dev/null +++ b/packages/@aws-cdk/assertions/lib/helpers-internal/index.ts @@ -0,0 +1,2 @@ +export * from '../match'; +export * from '../matcher'; diff --git a/packages/@aws-cdk/assertions/package.json b/packages/@aws-cdk/assertions/package.json index f93da67271e60..8da23f80292e7 100644 --- a/packages/@aws-cdk/assertions/package.json +++ b/packages/@aws-cdk/assertions/package.json @@ -18,6 +18,11 @@ "build+extract": "yarn build && yarn rosetta:extract", "build+test+extract": "yarn build+test && yarn rosetta:extract" }, + "ubergen": { + "exports": { + "./lib/helpers-internal": "./lib/helpers-internal/index.js" + } + }, "jsii": { "outdir": "dist", "diagnostics": { diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts index 77749ac8dfed6..6c642af83a866 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts @@ -1,11 +1,9 @@ /* eslint-disable no-console */ +// eslint-disable-next-line import/no-extraneous-dependencies +import { Match } from '@aws-cdk/assertions/lib/helpers-internal'; import { CustomResourceHandler } from './base'; import { AssertionResult, AssertionRequest } from './types'; -// this is needed for esbuild to work correctly -// eslint-disable-next-line @typescript-eslint/no-require-imports,import/no-extraneous-dependencies -const { Match } = require('@aws-cdk/assertions/lib/match'); - export class AssertionHandler extends CustomResourceHandler { protected async processEvent(request: AssertionRequest): Promise { let actual = request.actual; diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index d6593f26f7612..3c1ba81e19437 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -390,6 +390,7 @@ "./.jsii": "./.jsii", "./.warnings.jsii.js": "./.warnings.jsii.js", "./alexa-ask": "./alexa-ask/index.js", + "./assertions/lib/helpers-internal": "./assertions/lib/helpers-internal/index.js", "./assertions": "./assertions/index.js", "./assets": "./assets/index.js", "./aws-accessanalyzer": "./aws-accessanalyzer/index.js", @@ -504,6 +505,7 @@ "./aws-iotfleethub": "./aws-iotfleethub/index.js", "./aws-iotsitewise": "./aws-iotsitewise/index.js", "./aws-iotthingsgraph": "./aws-iotthingsgraph/index.js", + "./aws-iottwinmaker": "./aws-iottwinmaker/index.js", "./aws-iotwireless": "./aws-iotwireless/index.js", "./aws-ivs": "./aws-ivs/index.js", "./aws-kafkaconnect": "./aws-kafkaconnect/index.js", @@ -535,6 +537,7 @@ "./aws-medialive": "./aws-medialive/index.js", "./aws-mediapackage": "./aws-mediapackage/index.js", "./aws-mediastore": "./aws-mediastore/index.js", + "./aws-mediatailor": "./aws-mediatailor/index.js", "./aws-memorydb": "./aws-memorydb/index.js", "./aws-msk": "./aws-msk/index.js", "./aws-mwaa": "./aws-mwaa/index.js", From 8b730f235b2890daf25be857493e7aa61277b59f Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Wed, 4 May 2022 12:28:12 +0000 Subject: [PATCH 04/10] renaming SdkQuery to AwsApiCall --- .../test/integ.destinations.ts | 4 +-- packages/@aws-cdk/integ-tests/README.md | 8 +++--- .../integ-tests/lib/assertions/common.ts | 6 ++-- .../lib/assertions/deploy-assert.ts | 6 ++-- .../providers/lambda-handler/index.ts | 4 +-- .../providers/lambda-handler/sdk.ts | 6 ++-- .../providers/lambda-handler/types.ts | 4 +-- .../integ-tests/lib/assertions/sdk.ts | 18 ++++++------ .../integ-tests/rosetta/default.ts-fixture | 2 +- .../test/assertions/deploy-assert.test.ts | 8 +++--- .../providers/lambda-handler/sdk.test.ts | 14 +++++----- .../integ-tests/test/assertions/sdk.test.ts | 28 +++++++++---------- 12 files changed, 54 insertions(+), 54 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts b/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts index ad6c48384c9b6..ead9b1d771447 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts +++ b/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts @@ -2,9 +2,9 @@ import * as lambda from '@aws-cdk/aws-lambda'; import * as sns from '@aws-cdk/aws-sns'; import * as sqs from '@aws-cdk/aws-sqs'; import { App, Duration, Stack, StackProps } from '@aws-cdk/core'; +import { IntegTest, InvocationType, EqualsAssertion, AssertionType, ActualResult, ExpectedResult } from '@aws-cdk/integ-tests'; import { Construct } from 'constructs'; import * as destinations from '../lib'; -import { IntegTest, InvocationType, EqualsAssertion, AssertionType, ActualResult, ExpectedResult } from '@aws-cdk/integ-tests'; /* * Stack verification steps: @@ -85,7 +85,7 @@ const message = integ.assert.awsApiCall('SQS', 'receiveMessage', { }); new EqualsAssertion(integ.assert, 'ReceiveMessage', { - actual: ActualResult.fromSdkQuery(message, 'Messages.0.Body'), + actual: ActualResult.fromAwsApiCall(message, 'Messages.0.Body'), expected: ExpectedResult.fromObject({ requestContext: { condition: 'Success', diff --git a/packages/@aws-cdk/integ-tests/README.md b/packages/@aws-cdk/integ-tests/README.md index 364041efa421d..d3b982769f5d0 100644 --- a/packages/@aws-cdk/integ-tests/README.md +++ b/packages/@aws-cdk/integ-tests/README.md @@ -199,7 +199,7 @@ Any assertions that you create should be created in the scope of `DeployAssert`. declare const app: App; const assert = new DeployAssert(app); -new SdkQuery(assert, 'GetObject', { +new AwsApiCall(assert, 'GetObject', { service: 'S3', api: 'getObject', }); @@ -221,7 +221,7 @@ assert.strictEquals( In the above example an assertion is created that will trigger a user defined `CustomResource` and assert that the `data` attribute is equal to `{ foo: 'bar' }`. -### SdkQuery +### AwsApiCall A common method to retrieve the "actual" results to compare with what is expected is to make an AWS API call to receive some data. This library does this by utilizing CloudFormation custom resources @@ -233,7 +233,7 @@ This can be done by using the class directory: ```ts declare const assert: DeployAssert; -new SdkQuery(assert, 'MyAssertion', { +new AwsApiCall(assert, 'MyAssertion', { service: 'SQS', api: 'receiveMessage', parameters: { @@ -282,7 +282,7 @@ const message = integ.assert.awsApiCall('SQS', 'receiveMessage', { }); new EqualsAssertion(integ.assert, 'ReceiveMessage', { - actual: ActualResult.fromSdkQuery(message, 'Messages.0.Body'), + actual: ActualResult.fromAwsApiCall(message, 'Messages.0.Body'), expected: ExpectedResult.fromObject({ requestContext: { condition: 'Success', diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/common.ts b/packages/@aws-cdk/integ-tests/lib/assertions/common.ts index 2fa2fa2535630..8513cc4d68d63 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/common.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/common.ts @@ -1,5 +1,5 @@ import { CustomResource } from '@aws-cdk/core'; -import { SdkQuery } from './sdk'; +import { AwsApiCall } from './sdk'; /** * Represents the "actual" results to compare */ @@ -14,9 +14,9 @@ export abstract class ActualResult { } /** - * Get the actual results from a SdkQuery + * Get the actual results from a AwsApiCall */ - public static fromSdkQuery(query: SdkQuery, attribute: string): ActualResult { + public static fromAwsApiCall(query: AwsApiCall, attribute: string): ActualResult { return { result: query.getAttString(attribute), }; diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts b/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts index 7e68fa5294885..645a3f14135df 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts @@ -4,7 +4,7 @@ import { EqualsAssertion } from './assertions'; import { ExpectedResult, ActualResult } from './common'; import { md5hash } from './private/hash'; import { AssertionType } from './providers'; -import { SdkQuery, LambdaInvokeFunction, LambdaInvokeFunctionProps } from './sdk'; +import { AwsApiCall, LambdaInvokeFunction, LambdaInvokeFunctionProps } from './sdk'; const DEPLOY_ASSERT_SYMBOL = Symbol.for('@aws-cdk/integ-tests.DeployAssert'); @@ -72,8 +72,8 @@ export class DeployAssert extends CoreConstruct { * Messages: [{ Body: 'hello' }], * }); */ - public awsApiCall(service: string, api: string, parameters?: any): SdkQuery { - return new SdkQuery(this, `SdkQuery${service}${api}`, { + public awsApiCall(service: string, api: string, parameters?: any): AwsApiCall { + return new AwsApiCall(this, `AwsApiCall${service}${api}`, { api, service, parameters, diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/index.ts b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/index.ts index 07a1911efe4dd..78a47c83be1ef 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/index.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/index.ts @@ -1,6 +1,6 @@ import { AssertionHandler } from './assertion'; import { ResultsCollectionHandler } from './results'; -import { SdkHandler } from './sdk'; +import { AwsApiCallHandler } from './sdk'; import * as types from './types'; export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent, context: AWSLambda.Context) { @@ -10,7 +10,7 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent function createResourceHandler(event: AWSLambda.CloudFormationCustomResourceEvent, context: AWSLambda.Context) { if (event.ResourceType.startsWith(types.SDK_RESOURCE_TYPE_PREFIX)) { - return new SdkHandler(event, context); + return new AwsApiCallHandler(event, context); } switch (event.ResourceType) { case types.ASSERT_RESOURCE_TYPE: return new AssertionHandler(event, context); diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/sdk.ts b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/sdk.ts index a216b756e0374..5d53df2f5b38e 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/sdk.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/sdk.ts @@ -1,6 +1,6 @@ /* eslint-disable no-console */ import { CustomResourceHandler } from './base'; -import { SdkRequest, SdkResult } from './types'; +import { AwsApiCallRequest, AwsApiCallResult } from './types'; import { decode } from './utils'; /** @@ -25,8 +25,8 @@ export function flatten(object: object): { [key: string]: any } { } -export class SdkHandler extends CustomResourceHandler { - protected async processEvent(request: SdkRequest): Promise { +export class AwsApiCallHandler extends CustomResourceHandler { + protected async processEvent(request: AwsApiCallRequest): Promise { // eslint-disable-next-line const AWS: any = require('aws-sdk'); console.log(`AWS SDK VERSION: ${AWS.VERSION}`); diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/types.ts b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/types.ts index c6052dd2579bd..604b66146542d 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/types.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/types.ts @@ -8,7 +8,7 @@ export const SDK_RESOURCE_TYPE_PREFIX = 'Custom::DeployAssert@SdkCall'; /** * A AWS JavaScript SDK V2 request */ -export interface SdkRequest { +export interface AwsApiCallRequest { /** * The AWS service i.e. S3 */ @@ -48,7 +48,7 @@ export interface SdkRequest { /** * The result from a SdkQuery */ -export interface SdkResult { +export interface AwsApiCallResult { /** * The full api response */ diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts b/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts index 0913dbd94599a..72a8e0452648e 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts @@ -11,7 +11,7 @@ import { Construct as CoreConstruct } from '@aws-cdk/core'; /** * Options to perform an AWS JavaScript V2 API call */ -export interface SdkQueryOptions { +export interface AwsApiCallOptions { /** * The AWS service, i.e. S3 */ @@ -33,20 +33,20 @@ export interface SdkQueryOptions { /** * Options for creating an SDKQuery provider */ -export interface SdkQueryProps extends SdkQueryOptions {} +export interface AwsApiCallProps extends AwsApiCallOptions {} /** * Construct that creates a custom resource that will perform * a query using the AWS SDK */ -export class SdkQuery extends CoreConstruct { +export class AwsApiCall extends CoreConstruct { private readonly sdkCallResource: CustomResource; private flattenResponse: string = 'false'; private readonly name: string; protected provider: AssertionsProvider; - constructor(scope: Construct, id: string, props: SdkQueryProps) { + constructor(scope: Construct, id: string, props: AwsApiCallProps) { super(scope, id); this.provider = new AssertionsProvider(this, 'SdkProvider'); @@ -98,7 +98,7 @@ export class SdkQuery extends CoreConstruct { /** * Asserts that the expected value is strictly equal to - * the result of the SdkQuery. Missing fields will result in + * the result of the AwsApiCall. Missing fields will result in * a failure. */ public assertObjectEqual(expected: { [key: string]: any }): void { @@ -110,7 +110,7 @@ export class SdkQuery extends CoreConstruct { /** * Asserts that the expected value is a subset of - * the result of the SdkQuery + * the result of the AwsApiCall */ public assertObjectLike(expected: { [key: string]: any }): void { new EqualsAssertion(this, `EqualsAssertion${this.name}`, { @@ -122,7 +122,7 @@ export class SdkQuery extends CoreConstruct { /** * Asserts that the expected value is equal to the - * results of the SdkQuery. The result of the SdkQuery + * results of the AwsApiCall. The result of the SdkQuery * must be a string value. */ public assertStringEqual(expected: string): void { @@ -209,11 +209,11 @@ export interface LambdaInvokeFunctionProps { /** * An AWS Lambda Invoke function API call. - * Use this istead of the generic SdkQuery in order to + * Use this istead of the generic AwsApiCall in order to * invoke a lambda function. This will automatically create * the correct permissions to invoke the function */ -export class LambdaInvokeFunction extends SdkQuery { +export class LambdaInvokeFunction extends AwsApiCall { constructor(scope: Construct, id: string, props: LambdaInvokeFunctionProps) { super(scope, id, { api: 'invoke', diff --git a/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture b/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture index 0f49ebf717693..087886f752234 100644 --- a/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture @@ -4,7 +4,7 @@ import { IntegTest, IntegTestCaseStack, DeployAssert, - SdkQuery, + AwsApiCall, EqualsAssertion, ActualResult, ExpectedResult, diff --git a/packages/@aws-cdk/integ-tests/test/assertions/deploy-assert.test.ts b/packages/@aws-cdk/integ-tests/test/assertions/deploy-assert.test.ts index dec5ebafccd0f..47bb9f083dcdd 100644 --- a/packages/@aws-cdk/integ-tests/test/assertions/deploy-assert.test.ts +++ b/packages/@aws-cdk/integ-tests/test/assertions/deploy-assert.test.ts @@ -68,7 +68,7 @@ describe('DeployAssert', () => { deplossert.strictEquals( 'MyAssertion', ExpectedResult.fromString('foo'), - ActualResult.fromSdkQuery(query, 'att'), + ActualResult.fromAwsApiCall(query, 'att'), ); // THEN @@ -78,7 +78,7 @@ describe('DeployAssert', () => { assertionType: 'equals', actual: { 'Fn::GetAtt': [ - 'SdkQueryMyServiceMyApi', + 'AwsApiCallMyServiceMyApi', 'apiCallResponse.att', ], }, @@ -95,7 +95,7 @@ describe('DeployAssert', () => { deplossert.objectLike( 'MyAssertion', { foo: 'bar' }, - ActualResult.fromSdkQuery(query, 'att'), + ActualResult.fromAwsApiCall(query, 'att'), ); // THEN @@ -104,7 +104,7 @@ describe('DeployAssert', () => { expected: JSON.stringify({ foo: 'bar' }), actual: { 'Fn::GetAtt': [ - 'SdkQueryMyServiceMyApi', + 'AwsApiCallMyServiceMyApi', 'apiCallResponse.att', ], }, diff --git a/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/sdk.test.ts b/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/sdk.test.ts index bce5f29548cb8..c9d9c606d38d9 100644 --- a/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/sdk.test.ts +++ b/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/sdk.test.ts @@ -2,14 +2,14 @@ import * as SDK from 'aws-sdk'; import * as AWS from 'aws-sdk-mock'; import * as sinon from 'sinon'; -import { SdkRequest, SdkResult } from '../../../../lib/assertions'; -import { SdkHandler } from '../../../../lib/assertions/providers/lambda-handler/sdk'; +import { AwsApiCallRequest, AwsApiCallResult } from '../../../../lib/assertions'; +import { AwsApiCallHandler } from '../../../../lib/assertions/providers/lambda-handler/sdk'; function sdkHandler() { const context: any = { getRemainingTimeInMillis: () => 50000, }; - return new SdkHandler({} as any, context); // as any to ignore all type checks + return new AwsApiCallHandler({} as any, context); // as any to ignore all type checks } beforeAll(() => { jest.useFakeTimers(); @@ -45,7 +45,7 @@ describe('SdkHandler', () => { } as SDK.S3.ListObjectsOutput; AWS.mock('S3', 'listObjects', sinon.fake.resolves(expectedResponse)); const handler = sdkHandler() as any; - const request: SdkRequest = { + const request: AwsApiCallRequest = { service: 'S3', api: 'listObjects', parameters: { @@ -54,7 +54,7 @@ describe('SdkHandler', () => { }; // WHEN - const response: SdkResult = await handler.processEvent(request); + const response: AwsApiCallResult = await handler.processEvent(request); // THEN @@ -67,7 +67,7 @@ describe('SdkHandler', () => { const fake = sinon.fake.resolves({}); AWS.mock('EC2', 'describeInstances', fake); const handler = sdkHandler() as any; - const request: SdkRequest = { + const request: AwsApiCallRequest = { service: 'EC2', api: 'describeInstances', parameters: { @@ -88,7 +88,7 @@ describe('SdkHandler', () => { const fake = sinon.fake.resolves({}); AWS.mock('EC2', 'describeInstances', fake); const handler = sdkHandler() as any; - const request: SdkRequest = { + const request: AwsApiCallRequest = { service: 'EC2', api: 'describeInstances', parameters: { diff --git a/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts b/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts index 742c725d881ca..c847e3df1dffc 100644 --- a/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts +++ b/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts @@ -1,15 +1,15 @@ import { Template, Match } from '@aws-cdk/assertions'; import { App, Stack, CfnOutput } from '@aws-cdk/core'; -import { DeployAssert, SdkQuery, LambdaInvokeFunction, LogType, InvocationType } from '../../lib/assertions'; +import { DeployAssert, AwsApiCall, LambdaInvokeFunction, LogType, InvocationType } from '../../lib/assertions'; -describe('SdkQuery', () => { +describe('AwsApiCall', () => { test('default', () => { // GIVEN const app = new App(); const deplossert = new DeployAssert(app); // WHEN - new SdkQuery(deplossert, 'SdkQuery', { + new AwsApiCall(deplossert, 'AwsApiCall', { service: 'MyService', api: 'MyApi', }); @@ -30,7 +30,7 @@ describe('SdkQuery', () => { const deplossert = new DeployAssert(app); // WHEN - new SdkQuery(deplossert, 'SdkQuery', { + new AwsApiCall(deplossert, 'AwsApiCall', { service: 'MyService', api: 'MyApi', parameters: { @@ -59,7 +59,7 @@ describe('SdkQuery', () => { const deplossert = new DeployAssert(app); // WHEN - const query = new SdkQuery(deplossert, 'SdkQuery', { + const query = new AwsApiCall(deplossert, 'AwsApiCall', { service: 'MyService', api: 'MyApi', }); @@ -73,7 +73,7 @@ describe('SdkQuery', () => { template.hasOutput('GetAtt', { Value: { 'Fn::GetAtt': [ - 'SdkQuery', + 'AwsApiCall', 'apiCallResponse.att', ], }, @@ -91,7 +91,7 @@ describe('SdkQuery', () => { const deplossert = new DeployAssert(app); // WHEN - const query = new SdkQuery(deplossert, 'SdkQuery', { + const query = new AwsApiCall(deplossert, 'AwsApiCall', { service: 'MyService', api: 'MyApi', }); @@ -105,7 +105,7 @@ describe('SdkQuery', () => { template.hasOutput('GetAtt', { Value: { 'Fn::GetAtt': [ - 'SdkQuery', + 'AwsApiCall', 'apiCallResponse.att', ], }, @@ -127,7 +127,7 @@ describe('SdkQuery', () => { const deplossert = new DeployAssert(app); // WHEN - const query = new SdkQuery(deplossert, 'SdkQuery', { + const query = new AwsApiCall(deplossert, 'AwsApiCall', { service: 'MyService', api: 'MyApi', }); @@ -139,7 +139,7 @@ describe('SdkQuery', () => { expected: JSON.stringify({ foo: 'bar' }), actual: { 'Fn::GetAtt': [ - 'SdkQuery', + 'AwsApiCall', 'apiCallResponse', ], }, @@ -153,7 +153,7 @@ describe('SdkQuery', () => { const deplossert = new DeployAssert(app); // WHEN - const query = new SdkQuery(deplossert, 'SdkQuery', { + const query = new AwsApiCall(deplossert, 'AwsApiCall', { service: 'MyService', api: 'MyApi', }); @@ -165,7 +165,7 @@ describe('SdkQuery', () => { expected: JSON.stringify({ foo: 'bar' }), actual: { 'Fn::GetAtt': [ - 'SdkQuery', + 'AwsApiCall', 'apiCallResponse', ], }, @@ -179,7 +179,7 @@ describe('SdkQuery', () => { const deplossert = new DeployAssert(app); // WHEN - const query = new SdkQuery(deplossert, 'SdkQuery', { + const query = new AwsApiCall(deplossert, 'AwsApiCall', { service: 'MyService', api: 'MyApi', }); @@ -191,7 +191,7 @@ describe('SdkQuery', () => { expected: 'bar', actual: { 'Fn::GetAtt': [ - 'SdkQuery', + 'AwsApiCall', 'apiCallResponse', ], }, From 285434af0b1ffa3c394a17aebfe16287e1039a8a Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Wed, 4 May 2022 16:03:48 +0000 Subject: [PATCH 05/10] adding matcher utility --- .../aws-cdk-lambda-destinations.template.json | 22 +- .../test/destinations.integ.snapshot/cdk.out | 2 +- .../destinations.integ.snapshot/integ.json | 9 +- .../destinations.integ.snapshot/manifest.json | 102 +----- .../destinations.integ.snapshot/tree.json | 302 +----------------- .../test/integ.destinations.ts | 30 +- .../aws-lambda/test/integ.bundling.ts | 6 +- .../test/eventbridge/integ.put-events.ts | 6 +- .../integ-tests/lib/assertions/assertions.ts | 10 +- .../integ-tests/lib/assertions/common.ts | 33 +- .../lib/assertions/deploy-assert.ts | 19 +- .../integ-tests/lib/assertions/match.ts | 30 ++ .../providers/lambda-handler/assertion.ts | 129 ++++++-- .../providers/lambda-handler/types.ts | 5 - .../integ-tests/lib/assertions/sdk.ts | 74 +++-- .../test/assertions/deploy-assert.test.ts | 15 +- .../lambda-handler/assertion.test.ts | 143 +++++++-- .../integ-tests/test/assertions/sdk.test.ts | 17 +- 18 files changed, 368 insertions(+), 586 deletions(-) create mode 100644 packages/@aws-cdk/integ-tests/lib/assertions/match.ts diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/aws-cdk-lambda-destinations.template.json b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/aws-cdk-lambda-destinations.template.json index 84e4ea60c1c0e..87d704ed4327a 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/aws-cdk-lambda-destinations.template.json +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/aws-cdk-lambda-destinations.template.json @@ -80,7 +80,7 @@ "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "ZipFile": "exports.handler = async (event) => {\n if (event.status === 'OK') return 'success';\n throw new Error('failure');\n };" + "ZipFile": "exports.handler = async (event) => {\n if (event === 'OK') return 'success';\n throw new Error('failure');\n };" }, "Role": { "Fn::GetAtt": [ @@ -281,7 +281,7 @@ "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "ZipFile": "exports.handler = async (event) => {\n if (event.status === 'OK') return 'success';\n throw new Error('failure');\n };" + "ZipFile": "exports.handler = async (event) => {\n if (event === 'OK') return 'success';\n throw new Error('failure');\n };" }, "Role": { "Fn::GetAtt": [ @@ -391,23 +391,5 @@ "MaximumRetryAttempts": 0 } } - }, - "Outputs": { - "ExportsOutputRefSnsSqsC4810B27404A5AFF": { - "Value": { - "Ref": "SnsSqsC4810B27" - }, - "Export": { - "Name": "aws-cdk-lambda-destinations:ExportsOutputRefSnsSqsC4810B27404A5AFF" - } - }, - "ExportsOutputRefQueue4A7E3555425E8BD3": { - "Value": { - "Ref": "Queue4A7E3555" - }, - "Export": { - "Name": "aws-cdk-lambda-destinations:ExportsOutputRefQueue4A7E3555425E8BD3" - } - } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/cdk.out index 2efc89439fab8..90bef2e09ad39 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"18.0.0"} \ No newline at end of file +{"version":"17.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/integ.json b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/integ.json index 0add177715c8f..20b73bceabb1f 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/integ.json +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/integ.json @@ -1,11 +1,14 @@ { "version": "18.0.0", "testCases": { - "DefaultTestCase": { + "aws-lambda-destinations/test/integ.destinations": { "stacks": [ "aws-cdk-lambda-destinations" ], - "assertionStack": "IntegDefaultTestCaseDeployAssert175C057C" + "diffAssets": false, + "stackUpdateWorkflow": true } - } + }, + "synthContext": {}, + "enableLookups": false } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/manifest.json index 05160d2f6dfdb..2684d0546624f 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "18.0.0", + "version": "17.0.0", "artifacts": { "Tree": { "type": "cdk:tree", @@ -104,109 +104,9 @@ "type": "aws:cdk:logicalId", "data": "MySpecialAliasEventInvokeConfig05FF4E2F" } - ], - "/aws-cdk-lambda-destinations/Exports/Output{\"Ref\":\"SnsSqsC4810B27\"}": [ - { - "type": "aws:cdk:logicalId", - "data": "ExportsOutputRefSnsSqsC4810B27404A5AFF" - } - ], - "/aws-cdk-lambda-destinations/Exports/Output{\"Ref\":\"Queue4A7E3555\"}": [ - { - "type": "aws:cdk:logicalId", - "data": "ExportsOutputRefQueue4A7E3555425E8BD3" - } ] }, "displayName": "aws-cdk-lambda-destinations" - }, - "IntegDefaultTestCaseDeployAssert175C057C": { - "type": "aws:cloudformation:stack", - "environment": "aws://unknown-account/unknown-region", - "properties": { - "templateFile": "IntegDefaultTestCaseDeployAssert175C057C.template.json", - "validateOnSynth": false - }, - "dependencies": [ - "aws-cdk-lambda-destinations" - ], - "metadata": { - "/Integ/DefaultTestCase/DeployAssert": [ - { - "type": "aws:cdk:asset", - "data": { - "path": "asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle", - "id": "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", - "packaging": "zip", - "sourceHash": "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", - "s3BucketParameter": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB", - "s3KeyParameter": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4", - "artifactHashParameter": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0ArtifactHash4C05AFBC" - } - } - ], - "/Integ/DefaultTestCase/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/Default/Default": [ - { - "type": "aws:cdk:logicalId", - "data": "LambdaInvoked12df417a1b74909abb3ea643735a310" - } - ], - "/Integ/DefaultTestCase/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/Invoke": [ - { - "type": "aws:cdk:logicalId", - "data": "LambdaInvoked12df417a1b74909abb3ea643735a310InvokeF590C289" - } - ], - "/Integ/DefaultTestCase/DeployAssert/Default/SdkQuerySQSreceiveMessage/Default/Default": [ - { - "type": "aws:cdk:logicalId", - "data": "SdkQuerySQSreceiveMessage" - } - ], - "/Integ/DefaultTestCase/DeployAssert/Default/ReceiveMessage/Default/Default": [ - { - "type": "aws:cdk:logicalId", - "data": "ReceiveMessage" - } - ], - "/Integ/DefaultTestCase/DeployAssert/Default/ReceiveMessage/AssertionResults": [ - { - "type": "aws:cdk:logicalId", - "data": "AssertionResultsReceiveMessage" - } - ], - "/Integ/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role": [ - { - "type": "aws:cdk:logicalId", - "data": "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73" - } - ], - "/Integ/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler": [ - { - "type": "aws:cdk:logicalId", - "data": "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F" - } - ], - "/Integ/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3Bucket": [ - { - "type": "aws:cdk:logicalId", - "data": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB" - } - ], - "/Integ/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3VersionKey": [ - { - "type": "aws:cdk:logicalId", - "data": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4" - } - ], - "/Integ/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/ArtifactHash": [ - { - "type": "aws:cdk:logicalId", - "data": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0ArtifactHash4C05AFBC" - } - ] - }, - "displayName": "Integ/DefaultTestCase/DeployAssert" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/tree.json b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/tree.json index b85b047bc472a..60299ae021368 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/tree.json @@ -175,7 +175,7 @@ "aws:cdk:cloudformation:type": "AWS::Lambda::Function", "aws:cdk:cloudformation:props": { "code": { - "zipFile": "exports.handler = async (event) => {\n if (event.status === 'OK') return 'success';\n throw new Error('failure');\n };" + "zipFile": "exports.handler = async (event) => {\n if (event === 'OK') return 'success';\n throw new Error('failure');\n };" }, "role": { "Fn::GetAtt": [ @@ -495,7 +495,7 @@ "aws:cdk:cloudformation:type": "AWS::Lambda::Function", "aws:cdk:cloudformation:props": { "code": { - "zipFile": "exports.handler = async (event) => {\n if (event.status === 'OK') return 'success';\n throw new Error('failure');\n };" + "zipFile": "exports.handler = async (event) => {\n if (event === 'OK') return 'success';\n throw new Error('failure');\n };" }, "role": { "Fn::GetAtt": [ @@ -681,310 +681,12 @@ "fqn": "@aws-cdk/aws-lambda.Alias", "version": "0.0.0" } - }, - "Exports": { - "id": "Exports", - "path": "aws-cdk-lambda-destinations/Exports", - "children": { - "Output{\"Ref\":\"SnsSqsC4810B27\"}": { - "id": "Output{\"Ref\":\"SnsSqsC4810B27\"}", - "path": "aws-cdk-lambda-destinations/Exports/Output{\"Ref\":\"SnsSqsC4810B27\"}", - "constructInfo": { - "fqn": "@aws-cdk/core.CfnOutput", - "version": "0.0.0" - } - }, - "Output{\"Ref\":\"Queue4A7E3555\"}": { - "id": "Output{\"Ref\":\"Queue4A7E3555\"}", - "path": "aws-cdk-lambda-destinations/Exports/Output{\"Ref\":\"Queue4A7E3555\"}", - "constructInfo": { - "fqn": "@aws-cdk/core.CfnOutput", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/core.Construct", - "version": "0.0.0" - } } }, "constructInfo": { "fqn": "@aws-cdk/core.Stack", "version": "0.0.0" } - }, - "Integ": { - "id": "Integ", - "path": "Integ", - "children": { - "DefaultTestCase": { - "id": "DefaultTestCase", - "path": "Integ/DefaultTestCase", - "children": { - "DeployAssert": { - "id": "DeployAssert", - "path": "Integ/DefaultTestCase/DeployAssert", - "children": { - "Default": { - "id": "Default", - "path": "Integ/DefaultTestCase/DeployAssert/Default", - "children": { - "LambdaInvoked12df417a1b74909abb3ea643735a310": { - "id": "LambdaInvoked12df417a1b74909abb3ea643735a310", - "path": "Integ/DefaultTestCase/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310", - "children": { - "SdkProvider": { - "id": "SdkProvider", - "path": "Integ/DefaultTestCase/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/SdkProvider", - "children": { - "AssertionsProvider": { - "id": "AssertionsProvider", - "path": "Integ/DefaultTestCase/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/SdkProvider/AssertionsProvider", - "constructInfo": { - "fqn": "@aws-cdk/core.Construct", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/integ-tests.AssertionsProvider", - "version": "0.0.0" - } - }, - "Default": { - "id": "Default", - "path": "Integ/DefaultTestCase/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/Default", - "children": { - "Default": { - "id": "Default", - "path": "Integ/DefaultTestCase/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/Default/Default", - "constructInfo": { - "fqn": "@aws-cdk/core.CfnResource", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/core.CustomResource", - "version": "0.0.0" - } - }, - "Invoke": { - "id": "Invoke", - "path": "Integ/DefaultTestCase/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/Invoke", - "constructInfo": { - "fqn": "@aws-cdk/core.CfnResource", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/integ-tests.LambdaInvokeFunction", - "version": "0.0.0" - } - }, - "SdkQuerySQSreceiveMessage": { - "id": "SdkQuerySQSreceiveMessage", - "path": "Integ/DefaultTestCase/DeployAssert/Default/SdkQuerySQSreceiveMessage", - "children": { - "SdkProvider": { - "id": "SdkProvider", - "path": "Integ/DefaultTestCase/DeployAssert/Default/SdkQuerySQSreceiveMessage/SdkProvider", - "children": { - "AssertionsProvider": { - "id": "AssertionsProvider", - "path": "Integ/DefaultTestCase/DeployAssert/Default/SdkQuerySQSreceiveMessage/SdkProvider/AssertionsProvider", - "constructInfo": { - "fqn": "@aws-cdk/core.Construct", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/integ-tests.AssertionsProvider", - "version": "0.0.0" - } - }, - "Default": { - "id": "Default", - "path": "Integ/DefaultTestCase/DeployAssert/Default/SdkQuerySQSreceiveMessage/Default", - "children": { - "Default": { - "id": "Default", - "path": "Integ/DefaultTestCase/DeployAssert/Default/SdkQuerySQSreceiveMessage/Default/Default", - "constructInfo": { - "fqn": "@aws-cdk/core.CfnResource", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/core.CustomResource", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/integ-tests.SdkQuery", - "version": "0.0.0" - } - }, - "ReceiveMessage": { - "id": "ReceiveMessage", - "path": "Integ/DefaultTestCase/DeployAssert/Default/ReceiveMessage", - "children": { - "AssertionProvider": { - "id": "AssertionProvider", - "path": "Integ/DefaultTestCase/DeployAssert/Default/ReceiveMessage/AssertionProvider", - "children": { - "AssertionsProvider": { - "id": "AssertionsProvider", - "path": "Integ/DefaultTestCase/DeployAssert/Default/ReceiveMessage/AssertionProvider/AssertionsProvider", - "constructInfo": { - "fqn": "@aws-cdk/core.Construct", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/integ-tests.AssertionsProvider", - "version": "0.0.0" - } - }, - "Default": { - "id": "Default", - "path": "Integ/DefaultTestCase/DeployAssert/Default/ReceiveMessage/Default", - "children": { - "Default": { - "id": "Default", - "path": "Integ/DefaultTestCase/DeployAssert/Default/ReceiveMessage/Default/Default", - "constructInfo": { - "fqn": "@aws-cdk/core.CfnResource", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/core.CustomResource", - "version": "0.0.0" - } - }, - "AssertionResults": { - "id": "AssertionResults", - "path": "Integ/DefaultTestCase/DeployAssert/Default/ReceiveMessage/AssertionResults", - "constructInfo": { - "fqn": "@aws-cdk/core.CfnOutput", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/integ-tests.EqualsAssertion", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/integ-tests.DeployAssert", - "version": "0.0.0" - } - }, - "SingletonFunction1488541a7b23466481b69b4408076b81": { - "id": "SingletonFunction1488541a7b23466481b69b4408076b81", - "path": "Integ/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81", - "children": { - "Staging": { - "id": "Staging", - "path": "Integ/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Staging", - "constructInfo": { - "fqn": "@aws-cdk/core.AssetStaging", - "version": "0.0.0" - } - }, - "Role": { - "id": "Role", - "path": "Integ/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role", - "constructInfo": { - "fqn": "@aws-cdk/core.CfnResource", - "version": "0.0.0" - } - }, - "Handler": { - "id": "Handler", - "path": "Integ/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler", - "constructInfo": { - "fqn": "@aws-cdk/core.CfnResource", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/core.Construct", - "version": "0.0.0" - } - }, - "AssetParameters": { - "id": "AssetParameters", - "path": "Integ/DefaultTestCase/DeployAssert/AssetParameters", - "children": { - "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0": { - "id": "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", - "path": "Integ/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", - "children": { - "S3Bucket": { - "id": "S3Bucket", - "path": "Integ/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3Bucket", - "constructInfo": { - "fqn": "@aws-cdk/core.CfnParameter", - "version": "0.0.0" - } - }, - "S3VersionKey": { - "id": "S3VersionKey", - "path": "Integ/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3VersionKey", - "constructInfo": { - "fqn": "@aws-cdk/core.CfnParameter", - "version": "0.0.0" - } - }, - "ArtifactHash": { - "id": "ArtifactHash", - "path": "Integ/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/ArtifactHash", - "constructInfo": { - "fqn": "@aws-cdk/core.CfnParameter", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/core.Construct", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/core.Construct", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/core.Stack", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/integ-tests.IntegTestCase", - "version": "0.0.0" - } - } - }, - "constructInfo": { - "fqn": "@aws-cdk/integ-tests.IntegTest", - "version": "0.0.0" - } } }, "constructInfo": { diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts b/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts index ead9b1d771447..2408222d67827 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts +++ b/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts @@ -2,7 +2,7 @@ import * as lambda from '@aws-cdk/aws-lambda'; import * as sns from '@aws-cdk/aws-sns'; import * as sqs from '@aws-cdk/aws-sqs'; import { App, Duration, Stack, StackProps } from '@aws-cdk/core'; -import { IntegTest, InvocationType, EqualsAssertion, AssertionType, ActualResult, ExpectedResult } from '@aws-cdk/integ-tests'; +import { IntegTest, InvocationType, ExpectedResult } from '@aws-cdk/integ-tests'; import { Construct } from 'constructs'; import * as destinations from '../lib'; @@ -84,21 +84,17 @@ const message = integ.assert.awsApiCall('SQS', 'receiveMessage', { WaitTimeSeconds: 20, }); -new EqualsAssertion(integ.assert, 'ReceiveMessage', { - actual: ActualResult.fromAwsApiCall(message, 'Messages.0.Body'), - expected: ExpectedResult.fromObject({ - requestContext: { - condition: 'Success', - }, - requestPayload: { - status: 'OK', - }, - responseContext: { - statusCode: 200, - }, - responsePayload: 'success', - }), - assertionType: AssertionType.OBJECT_LIKE, -}); +message.assertAtPath('Messages.0.Body', ExpectedResult.objectLike({ + requestContext: { + condition: 'Success', + }, + requestPayload: { + status: 'OK', + }, + responseContext: { + statusCode: 200, + }, + responsePayload: 'success', +})); app.synth(); diff --git a/packages/@aws-cdk/aws-lambda/test/integ.bundling.ts b/packages/@aws-cdk/aws-lambda/test/integ.bundling.ts index c12b700ba3fbd..d13c18646a611 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.bundling.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.bundling.ts @@ -1,7 +1,7 @@ /// !cdk-integ pragma:disable-update-workflow import * as path from 'path'; import { App, Stack, StackProps } from '@aws-cdk/core'; -import { IntegTest } from '@aws-cdk/integ-tests'; +import { IntegTest, ExpectedResult } from '@aws-cdk/integ-tests'; import { Construct } from 'constructs'; import * as lambda from '../lib'; @@ -50,7 +50,7 @@ const integ = new IntegTest(app, 'IntegTest', { const invoke = integ.assert.invokeFunction({ functionName: stack.functionName, }); -invoke.assertObjectLike({ +invoke.assert(ExpectedResult.objectLike({ Payload: '200', -}); +})); app.synth(); diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/integ.put-events.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/integ.put-events.ts index 8e4664099283e..6f0b9f83a1f82 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/integ.put-events.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/integ.put-events.ts @@ -1,7 +1,7 @@ import * as events from '@aws-cdk/aws-events'; import * as sfn from '@aws-cdk/aws-stepfunctions'; import * as cdk from '@aws-cdk/core'; -import { IntegTest } from '@aws-cdk/integ-tests'; +import { IntegTest, ExpectedResult } from '@aws-cdk/integ-tests'; import { EventBridgePutEvents } from '../../lib'; /* @@ -56,8 +56,8 @@ const describe = testCase.assert.awsApiCall('StepFunctions', 'describeExecution' }); // assert the results -describe.assertObjectLike({ +describe.assert(ExpectedResult.objectLike({ status: 'SUCCEEDED', -}); +})); app.synth(); diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/assertions.ts b/packages/@aws-cdk/integ-tests/lib/assertions/assertions.ts index c18586a65ce59..2b593d177e6a7 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/assertions.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/assertions.ts @@ -1,7 +1,7 @@ import { CustomResource, CfnOutput } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { ExpectedResult, ActualResult } from './common'; -import { AssertionRequest, AssertionsProvider, ASSERT_RESOURCE_TYPE, AssertionType } from './providers'; +import { AssertionRequest, AssertionsProvider, ASSERT_RESOURCE_TYPE } from './providers'; // keep this import separate from other imports to reduce chance for merge conflicts with v2-main // eslint-disable-next-line no-duplicate-imports, import/order @@ -21,13 +21,6 @@ export interface EqualsAssertionProps { * The expected result to assert */ readonly expected: ExpectedResult; - - /** - * The type of assertion to make - * - * @default - AssertionType.DEFAULT - */ - readonly assertionType?: AssertionType; } /** @@ -47,7 +40,6 @@ export class EqualsAssertion extends CoreConstruct { const properties: AssertionRequest = { actual: props.actual.result, expected: props.expected.result, - assertionType: props.assertionType ?? AssertionType.EQUALS, }; const resource = new CustomResource(this, 'Default', { serviceToken: assertionProvider.serviceToken, diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/common.ts b/packages/@aws-cdk/integ-tests/lib/assertions/common.ts index 8513cc4d68d63..316d36848492d 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/common.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/common.ts @@ -32,22 +32,37 @@ export abstract class ActualResult { * Represents the "expected" results to compare */ export abstract class ExpectedResult { - /** - * Expected result is a string - */ - public static fromString(expected: string): ExpectedResult { + public static exact(expected: any): ExpectedResult { return { - result: expected, + result: JSON.stringify({ + $Exact: expected, + }), }; } + public static objectLike(expected: { [key: string]: any }): ExpectedResult { + return { + result: JSON.stringify({ + $ObjectLike: expected, + }), + }; + } + + public static arrayWith(expected: any[]): ExpectedResult { + return { + result: JSON.stringify({ + $ArrayWith: expected, + }), + }; + } /** - * Expected result is an object. The object will - * be stringified before being sent to the customResource + * Expected result is a string */ - public static fromObject(expected: { [key: string]: any }): ExpectedResult { + public static stringLikeRegexp(expected: string): ExpectedResult { return { - result: JSON.stringify(expected), + result: JSON.stringify({ + $StringLike: expected, + }), }; } diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts b/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts index 645a3f14135df..d66a76a5f2dd1 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts @@ -3,7 +3,6 @@ import { Construct, IConstruct, Node } from 'constructs'; import { EqualsAssertion } from './assertions'; import { ExpectedResult, ActualResult } from './common'; import { md5hash } from './private/hash'; -import { AssertionType } from './providers'; import { AwsApiCall, LambdaInvokeFunction, LambdaInvokeFunctionProps } from './sdk'; const DEPLOY_ASSERT_SYMBOL = Symbol.for('@aws-cdk/integ-tests.DeployAssert'); @@ -80,16 +79,6 @@ export class DeployAssert extends CoreConstruct { }); } - /** - * Assert that two values are strictly equal - */ - public strictEquals(id: string, expected: ExpectedResult, actual: ActualResult): void { - new EqualsAssertion(this, `EqualsAssertion${id}`, { - actual, - expected, - }); - } - /** * Invoke a lambda function and return the response which can be asserted * @@ -108,14 +97,10 @@ export class DeployAssert extends CoreConstruct { return new LambdaInvokeFunction(this, `LambdaInvoke${hash}`, props); } - /** - * Assert that an object is a subset of another - */ - public objectLike(id: string, expected: { [key: string]: any }, actual: ActualResult): void { + public assert(id: string, expected: ExpectedResult, actual: ActualResult): void { new EqualsAssertion(this, `EqualsAssertion${id}`, { + expected, actual, - expected: ExpectedResult.fromObject(expected), - assertionType: AssertionType.OBJECT_LIKE, }); } } diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/match.ts b/packages/@aws-cdk/integ-tests/lib/assertions/match.ts new file mode 100644 index 0000000000000..a736895021b63 --- /dev/null +++ b/packages/@aws-cdk/integ-tests/lib/assertions/match.ts @@ -0,0 +1,30 @@ + +/** + * Partial and special matching during assertions. + */ +export abstract class Match { + /** + * Matches the specified pattern with the array found in the same relative path of the target. + * The set of elements (or matchers) must be in the same order as would be found. + * @param pattern the pattern to match + */ + public static arrayWith(pattern: any[]): { [key: string]: any[] } { + return { $ArrayWith: pattern }; + } + + /** + * Matches the specified pattern to an object found in the same relative path of the target. + * The keys and their values (or matchers) must be present in the target but the target can be a superset. + * @param pattern the pattern to match + */ + public static objectLike(pattern: { [key: string]: any }): { [key: string]: { [key: string]: any } } { + return { $ObjectLike: pattern }; + } + + /** + * Matches targets according to a regular expression + */ + public static stringLikeRegexp(pattern: string): { [key: string]: string } { + return { $StringLike: pattern }; + } +} diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts index 6c642af83a866..1d49341c2980b 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts @@ -1,34 +1,17 @@ /* eslint-disable no-console */ // eslint-disable-next-line import/no-extraneous-dependencies -import { Match } from '@aws-cdk/assertions/lib/helpers-internal'; +import { Match, Matcher } from '@aws-cdk/assertions/lib/helpers-internal'; import { CustomResourceHandler } from './base'; import { AssertionResult, AssertionRequest } from './types'; export class AssertionHandler extends CustomResourceHandler { protected async processEvent(request: AssertionRequest): Promise { - let actual = request.actual; + let actual = decodeCall(request.actual); const expected = decodeCall(request.expected); let result: AssertionResult; - let matcher; + const matcher = new MatchCreator(expected).getMatcher(); console.log(`Testing equality between ${JSON.stringify(request.actual)} and ${JSON.stringify(request.expected)}`); - switch (request.assertionType) { - case 'objectLike': - if (typeof actual === 'string') { - actual = JSON.parse(actual); - } - matcher = Match.objectLike(expected); - break; - case 'equals': - matcher = Match.exact(expected); - break; - case 'arrayWith': - matcher = Match.arrayWith(expected); - break; - default: - throw new Error(`Unsupported query type ${request.assertionType}`); - } - const matchResult = matcher.test(actual); matchResult.finished(); if (matchResult.hasFailed()) { @@ -53,10 +36,112 @@ export class AssertionHandler extends CustomResourceHandler { }); describe('assertions', () => { - test('strictEquals', () => { + test('stringLike', () => { // GIVEN const app = new App(); const deplossert = new DeployAssert(app); const query = deplossert.awsApiCall('MyService', 'MyApi'); // WHEN - deplossert.strictEquals( + deplossert.assert( 'MyAssertion', - ExpectedResult.fromString('foo'), + ExpectedResult.stringLikeRegexp('foo'), ActualResult.fromAwsApiCall(query, 'att'), ); // THEN const template = Template.fromStack(Stack.of(deplossert)); template.hasResourceProperties('Custom::DeployAssert@AssertEquals', { - expected: 'foo', - assertionType: 'equals', + expected: JSON.stringify({ $StringLike: 'foo' }), actual: { 'Fn::GetAtt': [ 'AwsApiCallMyServiceMyApi', @@ -92,16 +91,16 @@ describe('DeployAssert', () => { const query = deplossert.awsApiCall('MyService', 'MyApi'); // WHEN - deplossert.objectLike( + deplossert.assert( 'MyAssertion', - { foo: 'bar' }, + ExpectedResult.objectLike({ foo: 'bar' }), ActualResult.fromAwsApiCall(query, 'att'), ); // THEN const template = Template.fromStack(Stack.of(deplossert)); template.hasResourceProperties('Custom::DeployAssert@AssertEquals', { - expected: JSON.stringify({ foo: 'bar' }), + expected: JSON.stringify({ $ObjectLike: { foo: 'bar' } }), actual: { 'Fn::GetAtt': [ 'AwsApiCallMyServiceMyApi', diff --git a/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/assertion.test.ts b/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/assertion.test.ts index 4b977e8d535c7..3c59b78099dbd 100644 --- a/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/assertion.test.ts +++ b/packages/@aws-cdk/integ-tests/test/assertions/providers/lambda-handler/assertion.test.ts @@ -1,4 +1,5 @@ -import { AssertionRequest, AssertionResult, AssertionType } from '../../../../lib/assertions'; +import { AssertionRequest, AssertionResult, ExpectedResult } from '../../../../lib/assertions'; +import { Match } from '../../../../lib/assertions/match'; import { AssertionHandler } from '../../../../lib/assertions/providers/lambda-handler/assertion'; function assertionHandler() { @@ -18,20 +19,122 @@ afterAll(() => { }); describe('AssertionHandler', () => { + describe('stringLike', () => { + test('pass', async () => { + // GIVEN + const handler = assertionHandler() as any; + const request: AssertionRequest = { + actual: 'this is the actual results', + expected: ExpectedResult.stringLikeRegexp('this is').result, + }; + + // WHEN + const response: AssertionResult = await handler.processEvent(request); + + // THEN + expect(response.data).toEqual('{"status":"pass"}'); + }); + + test('fail', async () => { + // GIVEN + const handler = assertionHandler() as any; + const request: AssertionRequest = { + actual: 'this is the actual results', + expected: ExpectedResult.stringLikeRegexp('abcd').result, + }; + + // WHEN + const response: AssertionResult = await handler.processEvent(request); + + // THEN + expect(JSON.parse(response.data)).toEqual({ + status: 'fail', + message: expect.stringMatching(/String 'this is the actual results' did not match pattern 'abcd' (using stringLikeRegexp matcher)*/), + }); + }); + }); + describe('arrayWith', () => { + test('pass', async () => { + // GIVEN + const handler = assertionHandler() as any; + const request: AssertionRequest = { + actual: [ + { + Elements: [{ Asdf: 3 }, { Asdf: 4 }], + }, + { + Elements: [{ Asdf: 2 }, { Asdf: 1 }], + }, + ], + expected: ExpectedResult.arrayWith([ + { + Elements: Match.arrayWith([{ Asdf: 3 }]), + }, + ]).result, + }; + + // WHEN + const response: AssertionResult = await handler.processEvent(request); + + // THEN + expect(response.data).toEqual('{"status":"pass"}'); + }); + + test('fail', async () => { + // GIVEN + const handler = assertionHandler() as any; + const request: AssertionRequest = { + actual: [ + { + Elements: [{ Asdf: 5 }, { Asdf: 4 }], + }, + { + Elements: [{ Asdf: 2 }, { Asdf: 1 }], + }, + ], + expected: ExpectedResult.arrayWith([ + { + Elements: [{ Asdf: 3 }], + }, + ]).result, + }; + + // WHEN + const response: AssertionResult = await handler.processEvent(request); + + // THEN + expect(JSON.parse(response.data)).toEqual({ + status: 'fail', + message: expect.stringMatching(/Missing element at pattern index 0 (using arrayWith matcher)*/), + }); + }); + }); + describe('objectLike', () => { test('pass', async () => { // GIVEN const handler = assertionHandler() as any; const request: AssertionRequest = { - assertionType: AssertionType.OBJECT_LIKE, actual: { - stringParam: 'foo', - numberParam: 3, - booleanParam: true, + Message: [ + { + OtherKey: 'value', + Payload: 'some status', + Body: { + OtherKey: 4, + Elements: [{ Asdf: 3 }, { Asdf: 4 }], + }, + }, + ], }, - expected: JSON.stringify({ - stringParam: 'foo', - }), + expected: ExpectedResult.objectLike({ + Message: [{ + Payload: Match.stringLikeRegexp('status'), + Body: Match.objectLike({ + Elements: Match.arrayWith([{ Asdf: 3 }]), + }), + }], + }).result, }; // WHEN @@ -45,15 +148,14 @@ describe('AssertionHandler', () => { // GIVEN const handler = assertionHandler() as any; const request: AssertionRequest = { - assertionType: AssertionType.OBJECT_LIKE, actual: { stringParam: 'foo', numberParam: 3, booleanParam: true, }, - expected: JSON.stringify({ + expected: ExpectedResult.objectLike({ stringParam: 'bar', - }), + }).result, }; // WHEN @@ -68,12 +170,11 @@ describe('AssertionHandler', () => { }); }); - describe('equals', () => { + describe('not using Match', () => { test('pass', async () => { // GIVEN const handler = assertionHandler() as any; const request: AssertionRequest = { - assertionType: AssertionType.EQUALS, actual: { stringParam: 'foo', numberParam: 3, @@ -97,7 +198,6 @@ describe('AssertionHandler', () => { // GIVEN const handler = assertionHandler() as any; const request: AssertionRequest = { - assertionType: AssertionType.EQUALS, actual: 'foo', expected: 'foo', }; @@ -113,7 +213,6 @@ describe('AssertionHandler', () => { // GIVEN const handler = assertionHandler() as any; const request: AssertionRequest = { - assertionType: AssertionType.EQUALS, actual: { stringParam: 'foo', }, @@ -132,18 +231,4 @@ describe('AssertionHandler', () => { }); }); }); - - test('unsupported query', async () => { - // GIVEN - const handler = assertionHandler() as any; - const assertionType: any = 'somethingElse'; - const request: AssertionRequest = { - assertionType, - actual: 'foo', - expected: JSON.stringify({ foo: 'bar' }), - }; - - // THEN - await expect(handler.processEvent(request)).rejects.toThrow(/Unsupported query type/); - }); }); diff --git a/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts b/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts index c847e3df1dffc..31f1bd5068a4b 100644 --- a/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts +++ b/packages/@aws-cdk/integ-tests/test/assertions/sdk.test.ts @@ -1,6 +1,6 @@ import { Template, Match } from '@aws-cdk/assertions'; import { App, Stack, CfnOutput } from '@aws-cdk/core'; -import { DeployAssert, AwsApiCall, LambdaInvokeFunction, LogType, InvocationType } from '../../lib/assertions'; +import { DeployAssert, AwsApiCall, LambdaInvokeFunction, LogType, InvocationType, ExpectedResult } from '../../lib/assertions'; describe('AwsApiCall', () => { test('default', () => { @@ -131,19 +131,18 @@ describe('AwsApiCall', () => { service: 'MyService', api: 'MyApi', }); - query.assertObjectEqual({ foo: 'bar' }); + query.assert(ExpectedResult.exact({ foo: 'bar' })); // THEN const template = Template.fromStack(Stack.of(deplossert)); template.hasResourceProperties('Custom::DeployAssert@AssertEquals', { - expected: JSON.stringify({ foo: 'bar' }), + expected: JSON.stringify({ $Exact: { foo: 'bar' } }), actual: { 'Fn::GetAtt': [ 'AwsApiCall', 'apiCallResponse', ], }, - assertionType: 'equals', }); }); @@ -157,19 +156,18 @@ describe('AwsApiCall', () => { service: 'MyService', api: 'MyApi', }); - query.assertObjectLike({ foo: 'bar' }); + query.assert(ExpectedResult.objectLike({ foo: 'bar' })); // THEN const template = Template.fromStack(Stack.of(deplossert)); template.hasResourceProperties('Custom::DeployAssert@AssertEquals', { - expected: JSON.stringify({ foo: 'bar' }), + expected: JSON.stringify({ $ObjectLike: { foo: 'bar' } }), actual: { 'Fn::GetAtt': [ 'AwsApiCall', 'apiCallResponse', ], }, - assertionType: 'objectLike', }); }); @@ -183,19 +181,18 @@ describe('AwsApiCall', () => { service: 'MyService', api: 'MyApi', }); - query.assertStringEqual('bar'); + query.assert(ExpectedResult.exact('bar')); // THEN const template = Template.fromStack(Stack.of(deplossert)); template.hasResourceProperties('Custom::DeployAssert@AssertEquals', { - expected: 'bar', + expected: JSON.stringify({ $Exact: 'bar' }), actual: { 'Fn::GetAtt': [ 'AwsApiCall', 'apiCallResponse', ], }, - assertionType: 'equals', }); }); }); From 74a9e398ef8d9d59bf88746e921d0d38f4139737 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Wed, 4 May 2022 19:20:10 +0000 Subject: [PATCH 06/10] updates --- ...ultTestDeployAssertCC49E667.template.json} | 37 +- .../index.js | 644 ++++++++++++++++++ .../index.js | 643 ----------------- .../aws-cdk-lambda-destinations.template.json | 22 +- .../test/destinations.integ.snapshot/cdk.out | 2 +- .../destinations.integ.snapshot/integ.json | 11 +- .../destinations.integ.snapshot/manifest.json | 102 ++- .../destinations.integ.snapshot/tree.json | 302 +++++++- .../test/integ.destinations.ts | 2 +- ...ultTestDeployAssertAACA0CAF.template.json} | 31 +- .../test/bundling.integ.snapshot/cdk.out | 2 +- .../test/bundling.integ.snapshot/integ.json | 6 +- .../bundling.integ.snapshot/manifest.json | 50 +- .../test/bundling.integ.snapshot/tree.json | 70 +- .../aws-lambda/test/integ.bundling.ts | 2 +- .../test/eventbridge/integ.put-events.ts | 2 +- ...ultTestDeployAssert1A6BA3F3.template.json} | 41 +- .../index.js | 644 ++++++++++++++++++ .../index.js | 643 ----------------- .../put-events.integ.snapshot/cdk.out | 2 +- .../put-events.integ.snapshot/integ.json | 6 +- .../put-events.integ.snapshot/manifest.json | 54 +- .../put-events.integ.snapshot/tree.json | 90 +-- .../lib/workers/extract/extract_worker.ts | 8 +- .../manifest.json | 2 +- .../manifest.json | 2 +- .../manifest.json | 2 +- packages/@aws-cdk/integ-tests/README.md | 36 +- .../integ-tests/lib/assertions/common.ts | 72 +- .../lib/assertions/deploy-assert.ts | 24 +- .../integ-tests/lib/assertions/index.ts | 1 + .../integ-tests/lib/assertions/sdk.ts | 81 +-- .../@aws-cdk/integ-tests/lib/test-case.ts | 6 +- .../integ-tests/rosetta/default.ts-fixture | 2 + .../test/manifest-synthesizer.test.ts | 10 +- 35 files changed, 2085 insertions(+), 1569 deletions(-) rename packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/{IntegDefaultTestCaseDeployAssert175C057C.template.json => DestinationsDefaultTestDeployAssertCC49E667.template.json} (75%) create mode 100644 packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/asset.1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b.bundle/index.js delete mode 100644 packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle/index.js rename packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/{IntegTestDefaultTestCaseDeployAssert4F885E41.template.json => BundlingDefaultTestDeployAssertAACA0CAF.template.json} (74%) rename packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/{IntegTestDefaultTestCaseDeployAssert4F885E41.template.json => PutEventsDefaultTestDeployAssert1A6BA3F3.template.json} (68%) create mode 100644 packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/asset.1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b.bundle/index.js delete mode 100644 packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle/index.js diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/IntegDefaultTestCaseDeployAssert175C057C.template.json b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/DestinationsDefaultTestDeployAssertCC49E667.template.json similarity index 75% rename from packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/IntegDefaultTestCaseDeployAssert175C057C.template.json rename to packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/DestinationsDefaultTestDeployAssertCC49E667.template.json index b0af5c7e09ddf..57bd8ef6a2df0 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/IntegDefaultTestCaseDeployAssert175C057C.template.json +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/DestinationsDefaultTestDeployAssertCC49E667.template.json @@ -19,7 +19,7 @@ "Payload": "{\"status\":\"OK\"}" }, "flattenResponse": "false", - "salt": "1651499763724" + "salt": "1651691787842" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -39,7 +39,7 @@ } } }, - "SdkQuerySQSreceiveMessage": { + "AwsApiCallSQSreceiveMessage": { "Type": "Custom::DeployAssert@SdkCallSQSreceiveMessage", "Properties": { "ServiceToken": { @@ -57,12 +57,12 @@ "WaitTimeSeconds": 20 }, "flattenResponse": "true", - "salt": "1651499763725" + "salt": "1651691787842" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "ReceiveMessage": { + "AwsApiCallSQSreceiveMessageAssertEqualsSQSreceiveMessage56120636": { "Type": "Custom::DeployAssert@AssertEquals", "Properties": { "ServiceToken": { @@ -73,13 +73,12 @@ }, "actual": { "Fn::GetAtt": [ - "SdkQuerySQSreceiveMessage", + "AwsApiCallSQSreceiveMessage", "apiCallResponse.Messages.0.Body" ] }, - "expected": "{\"requestContext\":{\"condition\":\"Success\"},\"requestPayload\":{\"status\":\"OK\"},\"responseContext\":{\"statusCode\":200},\"responsePayload\":\"success\"}", - "assertionType": "objectLike", - "salt": "1651499763725" + "expected": "{\"$ObjectLike\":{\"requestContext\":{\"condition\":\"Success\"},\"requestPayload\":{\"status\":\"OK\"},\"responseContext\":{\"statusCode\":200},\"responsePayload\":\"success\"}}", + "salt": "1651691787843" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -171,7 +170,7 @@ "Runtime": "nodejs14.x", "Code": { "S3Bucket": { - "Ref": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB" + "Ref": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3BucketF7210344" }, "S3Key": { "Fn::Join": [ @@ -184,7 +183,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4" + "Ref": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3VersionKey1E71961C" } ] } @@ -197,7 +196,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4" + "Ref": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3VersionKey1E71961C" } ] } @@ -219,27 +218,27 @@ } }, "Outputs": { - "AssertionResultsReceiveMessage": { + "AssertionResultsAssertEqualsSQSreceiveMessage": { "Value": { "Fn::GetAtt": [ - "ReceiveMessage", + "AwsApiCallSQSreceiveMessageAssertEqualsSQSreceiveMessage56120636", "data" ] } } }, "Parameters": { - "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB": { + "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3BucketF7210344": { "Type": "String", - "Description": "S3 bucket for asset \"37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0\"" + "Description": "S3 bucket for asset \"1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b\"" }, - "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4": { + "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3VersionKey1E71961C": { "Type": "String", - "Description": "S3 key for asset version \"37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0\"" + "Description": "S3 key for asset version \"1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b\"" }, - "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0ArtifactHash4C05AFBC": { + "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bArtifactHash4F8362F2": { "Type": "String", - "Description": "Artifact hash for asset \"37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0\"" + "Description": "Artifact hash for asset \"1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/asset.1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b.bundle/index.js b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/asset.1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b.bundle/index.js new file mode 100644 index 0000000000000..32e3e2c1e5a95 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/asset.1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b.bundle/index.js @@ -0,0 +1,644 @@ +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getOwnPropSymbols = Object.getOwnPropertySymbols; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __propIsEnum = Object.prototype.propertyIsEnumerable; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __spreadValues = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + if (__getOwnPropSymbols) + for (var prop of __getOwnPropSymbols(b)) { + if (__propIsEnum.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + } + return a; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// lib/assertions/providers/lambda-handler/index.ts +var lambda_handler_exports = {}; +__export(lambda_handler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(lambda_handler_exports); + +// ../assertions/lib/matcher.ts +var Matcher = class { + static isMatcher(x) { + return x && x instanceof Matcher; + } +}; +var MatchResult = class { + constructor(target) { + this.failures = []; + this.captures = /* @__PURE__ */ new Map(); + this.finalized = false; + this.target = target; + } + push(matcher, path, message) { + return this.recordFailure({ matcher, path, message }); + } + recordFailure(failure) { + this.failures.push(failure); + return this; + } + hasFailed() { + return this.failures.length !== 0; + } + get failCount() { + return this.failures.length; + } + compose(id, inner) { + const innerF = inner.failures; + this.failures.push(...innerF.map((f) => { + return { path: [id, ...f.path], message: f.message, matcher: f.matcher }; + })); + inner.captures.forEach((vals, capture) => { + vals.forEach((value) => this.recordCapture({ capture, value })); + }); + return this; + } + finished() { + if (this.finalized) { + return this; + } + if (this.failCount === 0) { + this.captures.forEach((vals, cap) => cap._captured.push(...vals)); + } + this.finalized = true; + return this; + } + toHumanStrings() { + return this.failures.map((r) => { + const loc = r.path.length === 0 ? "" : ` at ${r.path.join("")}`; + return "" + r.message + loc + ` (using ${r.matcher.name} matcher)`; + }); + } + recordCapture(options) { + let values = this.captures.get(options.capture); + if (values === void 0) { + values = []; + } + values.push(options.value); + this.captures.set(options.capture, values); + } +}; + +// ../assertions/lib/private/matchers/absent.ts +var AbsentMatch = class extends Matcher { + constructor(name) { + super(); + this.name = name; + } + test(actual) { + const result = new MatchResult(actual); + if (actual !== void 0) { + result.recordFailure({ + matcher: this, + path: [], + message: `Received ${actual}, but key should be absent` + }); + } + return result; + } +}; + +// ../assertions/lib/private/type.ts +function getType(obj) { + return Array.isArray(obj) ? "array" : typeof obj; +} + +// ../assertions/lib/match.ts +var Match = class { + static absent() { + return new AbsentMatch("absent"); + } + static arrayWith(pattern) { + return new ArrayMatch("arrayWith", pattern); + } + static arrayEquals(pattern) { + return new ArrayMatch("arrayEquals", pattern, { subsequence: false }); + } + static exact(pattern) { + return new LiteralMatch("exact", pattern, { partialObjects: false }); + } + static objectLike(pattern) { + return new ObjectMatch("objectLike", pattern); + } + static objectEquals(pattern) { + return new ObjectMatch("objectEquals", pattern, { partial: false }); + } + static not(pattern) { + return new NotMatch("not", pattern); + } + static serializedJson(pattern) { + return new SerializedJson("serializedJson", pattern); + } + static anyValue() { + return new AnyMatch("anyValue"); + } + static stringLikeRegexp(pattern) { + return new StringLikeRegexpMatch("stringLikeRegexp", pattern); + } +}; +var LiteralMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.partialObjects = options.partialObjects ?? false; + if (Matcher.isMatcher(this.pattern)) { + throw new Error("LiteralMatch cannot directly contain another matcher. Remove the top-level matcher or nest it more deeply."); + } + } + test(actual) { + if (Array.isArray(this.pattern)) { + return new ArrayMatch(this.name, this.pattern, { subsequence: false, partialObjects: this.partialObjects }).test(actual); + } + if (typeof this.pattern === "object") { + return new ObjectMatch(this.name, this.pattern, { partial: this.partialObjects }).test(actual); + } + const result = new MatchResult(actual); + if (typeof this.pattern !== typeof actual) { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected type ${typeof this.pattern} but received ${getType(actual)}` + }); + return result; + } + if (actual !== this.pattern) { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected ${this.pattern} but received ${actual}` + }); + } + return result; + } +}; +var ArrayMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.subsequence = options.subsequence ?? true; + this.partialObjects = options.partialObjects ?? false; + } + test(actual) { + if (!Array.isArray(actual)) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected type array but received ${getType(actual)}` + }); + } + if (!this.subsequence && this.pattern.length !== actual.length) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected array of length ${this.pattern.length} but received ${actual.length}` + }); + } + let patternIdx = 0; + let actualIdx = 0; + const result = new MatchResult(actual); + while (patternIdx < this.pattern.length && actualIdx < actual.length) { + const patternElement = this.pattern[patternIdx]; + const matcher = Matcher.isMatcher(patternElement) ? patternElement : new LiteralMatch(this.name, patternElement, { partialObjects: this.partialObjects }); + const matcherName = matcher.name; + if (this.subsequence && (matcherName == "absent" || matcherName == "anyValue")) { + throw new Error(`The Matcher ${matcherName}() cannot be nested within arrayWith()`); + } + const innerResult = matcher.test(actual[actualIdx]); + if (!this.subsequence || !innerResult.hasFailed()) { + result.compose(`[${actualIdx}]`, innerResult); + patternIdx++; + actualIdx++; + } else { + actualIdx++; + } + } + for (; patternIdx < this.pattern.length; patternIdx++) { + const pattern = this.pattern[patternIdx]; + const element = Matcher.isMatcher(pattern) || typeof pattern === "object" ? " " : ` [${pattern}] `; + result.recordFailure({ + matcher: this, + path: [], + message: `Missing element${element}at pattern index ${patternIdx}` + }); + } + return result; + } +}; +var ObjectMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.partial = options.partial ?? true; + } + test(actual) { + if (typeof actual !== "object" || Array.isArray(actual)) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected type object but received ${getType(actual)}` + }); + } + const result = new MatchResult(actual); + if (!this.partial) { + for (const a of Object.keys(actual)) { + if (!(a in this.pattern)) { + result.recordFailure({ + matcher: this, + path: [`/${a}`], + message: "Unexpected key" + }); + } + } + } + for (const [patternKey, patternVal] of Object.entries(this.pattern)) { + if (!(patternKey in actual) && !(patternVal instanceof AbsentMatch)) { + result.recordFailure({ + matcher: this, + path: [`/${patternKey}`], + message: `Missing key '${patternKey}' among {${Object.keys(actual).join(",")}}` + }); + continue; + } + const matcher = Matcher.isMatcher(patternVal) ? patternVal : new LiteralMatch(this.name, patternVal, { partialObjects: this.partial }); + const inner = matcher.test(actual[patternKey]); + result.compose(`/${patternKey}`, inner); + } + return result; + } +}; +var SerializedJson = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const result = new MatchResult(actual); + if (getType(actual) !== "string") { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected JSON as a string but found ${getType(actual)}` + }); + return result; + } + let parsed; + try { + parsed = JSON.parse(actual); + } catch (err) { + if (err instanceof SyntaxError) { + result.recordFailure({ + matcher: this, + path: [], + message: `Invalid JSON string: ${actual}` + }); + return result; + } else { + throw err; + } + } + const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); + const innerResult = matcher.test(parsed); + result.compose(`(${this.name})`, innerResult); + return result; + } +}; +var NotMatch = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); + const innerResult = matcher.test(actual); + const result = new MatchResult(actual); + if (innerResult.failCount === 0) { + result.recordFailure({ + matcher: this, + path: [], + message: `Found unexpected match: ${JSON.stringify(actual, void 0, 2)}` + }); + } + return result; + } +}; +var AnyMatch = class extends Matcher { + constructor(name) { + super(); + this.name = name; + } + test(actual) { + const result = new MatchResult(actual); + if (actual == null) { + result.recordFailure({ + matcher: this, + path: [], + message: "Expected a value but found none" + }); + } + return result; + } +}; +var StringLikeRegexpMatch = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const result = new MatchResult(actual); + const regex = new RegExp(this.pattern, "gm"); + if (typeof actual !== "string") { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected a string, but got '${typeof actual}'` + }); + } + if (!regex.test(actual)) { + result.recordFailure({ + matcher: this, + path: [], + message: `String '${actual}' did not match pattern '${this.pattern}'` + }); + } + return result; + } +}; + +// lib/assertions/providers/lambda-handler/base.ts +var https = __toESM(require("https")); +var url = __toESM(require("url")); +var CustomResourceHandler = class { + constructor(event, context) { + this.event = event; + this.context = context; + this.timedOut = false; + this.timeout = setTimeout(async () => { + await this.respond({ + status: "FAILED", + reason: "Lambda Function Timeout", + data: this.context.logStreamName + }); + this.timedOut = true; + }, context.getRemainingTimeInMillis() - 1200); + this.event = event; + this.physicalResourceId = extractPhysicalResourceId(event); + } + async handle() { + try { + console.log(`Event: ${JSON.stringify(this.event)}`); + const response = await this.processEvent(this.event.ResourceProperties); + console.log(`Event output : ${JSON.stringify(response)}`); + await this.respond({ + status: "SUCCESS", + reason: "OK", + data: response + }); + } catch (e) { + console.log(e); + await this.respond({ + status: "FAILED", + reason: e.message ?? "Internal Error" + }); + } finally { + clearTimeout(this.timeout); + } + } + respond(response) { + if (this.timedOut) { + return; + } + const cfResponse = { + Status: response.status, + Reason: response.reason, + PhysicalResourceId: this.physicalResourceId, + StackId: this.event.StackId, + RequestId: this.event.RequestId, + LogicalResourceId: this.event.LogicalResourceId, + NoEcho: false, + Data: response.data + }; + const responseBody = JSON.stringify(cfResponse); + console.log("Responding to CloudFormation", responseBody); + const parsedUrl = url.parse(this.event.ResponseURL); + const requestOptions = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: "PUT", + headers: { "content-type": "", "content-length": responseBody.length } + }; + return new Promise((resolve, reject) => { + try { + const request2 = https.request(requestOptions, resolve); + request2.on("error", reject); + request2.write(responseBody); + request2.end(); + } catch (e) { + reject(e); + } + }); + } +}; +function extractPhysicalResourceId(event) { + switch (event.RequestType) { + case "Create": + return event.LogicalResourceId; + case "Update": + case "Delete": + return event.PhysicalResourceId; + } +} + +// lib/assertions/providers/lambda-handler/assertion.ts +var AssertionHandler = class extends CustomResourceHandler { + async processEvent(request2) { + let actual = decodeCall(request2.actual); + const expected = decodeCall(request2.expected); + let result; + const matcher = new MatchCreator(expected).getMatcher(); + console.log(`Testing equality between ${JSON.stringify(request2.actual)} and ${JSON.stringify(request2.expected)}`); + const matchResult = matcher.test(actual); + matchResult.finished(); + if (matchResult.hasFailed()) { + result = { + data: JSON.stringify({ + status: "fail", + message: [ + ...matchResult.toHumanStrings(), + JSON.stringify(matchResult.target, void 0, 2) + ].join("\n") + }) + }; + } else { + result = { + data: JSON.stringify({ + status: "pass" + }) + }; + } + return result; + } +}; +var MatchCreator = class { + constructor(obj) { + switch (Object.keys(obj)[0]) { + case "$ObjectLike": + this.type = "objectLike"; + this.parsedObj = obj.$ObjectLike; + break; + case "$ArrayWith": + this.type = "arrayWith"; + this.parsedObj = obj.$ArrayWith; + break; + case "$Exact": + this.type = "exact"; + this.parsedObj = obj.$Exact; + break; + case "$StringLike": + this.type = "stringLikeRegexp"; + this.parsedObj = obj.$StringLike; + break; + default: + this.type = "exact"; + this.parsedObj = obj; + } + } + getMatcher() { + try { + const final = JSON.parse(JSON.stringify(this.parsedObj), function(_k, v) { + const nested = Object.keys(v)[0]; + switch (nested) { + case "$ArrayWith": + return Match.arrayWith(v[nested]); + case "$ObjectLike": + return Match.objectLike(v[nested]); + case "$StringLike": + return Match.stringLikeRegexp(v[nested]); + default: + return v; + } + }); + return Match[this.type](final); + } catch { + return Match[this.type](this.parsedObj); + } + } +}; +function decodeCall(call) { + if (!call) { + return void 0; + } + try { + const parsed = JSON.parse(call); + return parsed; + } catch (e) { + return call; + } +} + +// lib/assertions/providers/lambda-handler/results.ts +var ResultsCollectionHandler = class extends CustomResourceHandler { + async processEvent(request2) { + const reduced = request2.assertionResults.reduce((agg, result, idx) => { + const msg = result.status === "pass" ? "pass" : `fail - ${result.message}`; + return `${agg} +Test${idx}: ${msg}`; + }, "").trim(); + return { message: reduced }; + } +}; + +// lib/assertions/providers/lambda-handler/utils.ts +function decode(object) { + return JSON.parse(JSON.stringify(object), (_k, v) => { + switch (v) { + case "TRUE:BOOLEAN": + return true; + case "FALSE:BOOLEAN": + return false; + default: + return v; + } + }); +} + +// lib/assertions/providers/lambda-handler/sdk.ts +function flatten(object) { + return Object.assign({}, ...function _flatten(child, path = []) { + return [].concat(...Object.keys(child).map((key) => { + const childKey = Buffer.isBuffer(child[key]) ? child[key].toString("utf8") : child[key]; + return typeof childKey === "object" && childKey !== null ? _flatten(childKey, path.concat([key])) : { [path.concat([key]).join(".")]: childKey }; + })); + }(object)); +} +var AwsApiCallHandler = class extends CustomResourceHandler { + async processEvent(request2) { + const AWS = require("aws-sdk"); + console.log(`AWS SDK VERSION: ${AWS.VERSION}`); + const service = new AWS[request2.service](); + const response = await service[request2.api](request2.parameters && decode(request2.parameters)).promise(); + console.log(`SDK response received ${JSON.stringify(response)}`); + delete response.ResponseMetadata; + const respond = { + apiCallResponse: response + }; + const flatData = __spreadValues({}, flatten(respond)); + return request2.flattenResponse === "true" ? flatData : respond; + } +}; + +// lib/assertions/providers/lambda-handler/types.ts +var ASSERT_RESOURCE_TYPE = "Custom::DeployAssert@AssertEquals"; +var RESULTS_RESOURCE_TYPE = "Custom::DeployAssert@ResultsCollection"; +var SDK_RESOURCE_TYPE_PREFIX = "Custom::DeployAssert@SdkCall"; + +// lib/assertions/providers/lambda-handler/index.ts +async function handler(event, context) { + const provider = createResourceHandler(event, context); + await provider.handle(); +} +function createResourceHandler(event, context) { + if (event.ResourceType.startsWith(SDK_RESOURCE_TYPE_PREFIX)) { + return new AwsApiCallHandler(event, context); + } + switch (event.ResourceType) { + case ASSERT_RESOURCE_TYPE: + return new AssertionHandler(event, context); + case RESULTS_RESOURCE_TYPE: + return new ResultsCollectionHandler(event, context); + default: + throw new Error(`Unsupported resource type "${event.ResourceType}`); + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle/index.js b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle/index.js deleted file mode 100644 index 3a860016dea59..0000000000000 --- a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle/index.js +++ /dev/null @@ -1,643 +0,0 @@ -var __create = Object.create; -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __getOwnPropSymbols = Object.getOwnPropertySymbols; -var __getProtoOf = Object.getPrototypeOf; -var __hasOwnProp = Object.prototype.hasOwnProperty; -var __propIsEnum = Object.prototype.propertyIsEnumerable; -var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; -var __spreadValues = (a, b) => { - for (var prop in b || (b = {})) - if (__hasOwnProp.call(b, prop)) - __defNormalProp(a, prop, b[prop]); - if (__getOwnPropSymbols) - for (var prop of __getOwnPropSymbols(b)) { - if (__propIsEnum.call(b, prop)) - __defNormalProp(a, prop, b[prop]); - } - return a; -}; -var __esm = (fn, res) => function __init() { - return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; -}; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { get: all[name], enumerable: true }); -}; -var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; -}; -var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); -var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); - -// ../assertions/lib/matcher.ts -var Matcher, MatchResult; -var init_matcher = __esm({ - "../assertions/lib/matcher.ts"() { - Matcher = class { - static isMatcher(x) { - return x && x instanceof Matcher; - } - }; - MatchResult = class { - constructor(target) { - this.failures = []; - this.captures = /* @__PURE__ */ new Map(); - this.finalized = false; - this.target = target; - } - push(matcher, path, message) { - return this.recordFailure({ matcher, path, message }); - } - recordFailure(failure) { - this.failures.push(failure); - return this; - } - hasFailed() { - return this.failures.length !== 0; - } - get failCount() { - return this.failures.length; - } - compose(id, inner) { - const innerF = inner.failures; - this.failures.push(...innerF.map((f) => { - return { path: [id, ...f.path], message: f.message, matcher: f.matcher }; - })); - inner.captures.forEach((vals, capture) => { - vals.forEach((value) => this.recordCapture({ capture, value })); - }); - return this; - } - finished() { - if (this.finalized) { - return this; - } - if (this.failCount === 0) { - this.captures.forEach((vals, cap) => cap._captured.push(...vals)); - } - this.finalized = true; - return this; - } - toHumanStrings() { - return this.failures.map((r) => { - const loc = r.path.length === 0 ? "" : ` at ${r.path.join("")}`; - return "" + r.message + loc + ` (using ${r.matcher.name} matcher)`; - }); - } - recordCapture(options) { - let values = this.captures.get(options.capture); - if (values === void 0) { - values = []; - } - values.push(options.value); - this.captures.set(options.capture, values); - } - }; - } -}); - -// ../assertions/lib/private/matchers/absent.ts -var AbsentMatch; -var init_absent = __esm({ - "../assertions/lib/private/matchers/absent.ts"() { - init_matcher(); - AbsentMatch = class extends Matcher { - constructor(name) { - super(); - this.name = name; - } - test(actual) { - const result = new MatchResult(actual); - if (actual !== void 0) { - result.recordFailure({ - matcher: this, - path: [], - message: `Received ${actual}, but key should be absent` - }); - } - return result; - } - }; - } -}); - -// ../assertions/lib/private/type.ts -function getType(obj) { - return Array.isArray(obj) ? "array" : typeof obj; -} -var init_type = __esm({ - "../assertions/lib/private/type.ts"() { - } -}); - -// ../assertions/lib/match.ts -var match_exports = {}; -__export(match_exports, { - Match: () => Match -}); -var Match, LiteralMatch, ArrayMatch, ObjectMatch, SerializedJson, NotMatch, AnyMatch, StringLikeRegexpMatch; -var init_match = __esm({ - "../assertions/lib/match.ts"() { - init_matcher(); - init_absent(); - init_type(); - Match = class { - static absent() { - return new AbsentMatch("absent"); - } - static arrayWith(pattern) { - return new ArrayMatch("arrayWith", pattern); - } - static arrayEquals(pattern) { - return new ArrayMatch("arrayEquals", pattern, { subsequence: false }); - } - static exact(pattern) { - return new LiteralMatch("exact", pattern, { partialObjects: false }); - } - static objectLike(pattern) { - return new ObjectMatch("objectLike", pattern); - } - static objectEquals(pattern) { - return new ObjectMatch("objectEquals", pattern, { partial: false }); - } - static not(pattern) { - return new NotMatch("not", pattern); - } - static serializedJson(pattern) { - return new SerializedJson("serializedJson", pattern); - } - static anyValue() { - return new AnyMatch("anyValue"); - } - static stringLikeRegexp(pattern) { - return new StringLikeRegexpMatch("stringLikeRegexp", pattern); - } - }; - LiteralMatch = class extends Matcher { - constructor(name, pattern, options = {}) { - super(); - this.name = name; - this.pattern = pattern; - this.partialObjects = options.partialObjects ?? false; - if (Matcher.isMatcher(this.pattern)) { - throw new Error("LiteralMatch cannot directly contain another matcher. Remove the top-level matcher or nest it more deeply."); - } - } - test(actual) { - if (Array.isArray(this.pattern)) { - return new ArrayMatch(this.name, this.pattern, { subsequence: false, partialObjects: this.partialObjects }).test(actual); - } - if (typeof this.pattern === "object") { - return new ObjectMatch(this.name, this.pattern, { partial: this.partialObjects }).test(actual); - } - const result = new MatchResult(actual); - if (typeof this.pattern !== typeof actual) { - result.recordFailure({ - matcher: this, - path: [], - message: `Expected type ${typeof this.pattern} but received ${getType(actual)}` - }); - return result; - } - if (actual !== this.pattern) { - result.recordFailure({ - matcher: this, - path: [], - message: `Expected ${this.pattern} but received ${actual}` - }); - } - return result; - } - }; - ArrayMatch = class extends Matcher { - constructor(name, pattern, options = {}) { - super(); - this.name = name; - this.pattern = pattern; - this.subsequence = options.subsequence ?? true; - this.partialObjects = options.partialObjects ?? false; - } - test(actual) { - if (!Array.isArray(actual)) { - return new MatchResult(actual).recordFailure({ - matcher: this, - path: [], - message: `Expected type array but received ${getType(actual)}` - }); - } - if (!this.subsequence && this.pattern.length !== actual.length) { - return new MatchResult(actual).recordFailure({ - matcher: this, - path: [], - message: `Expected array of length ${this.pattern.length} but received ${actual.length}` - }); - } - let patternIdx = 0; - let actualIdx = 0; - const result = new MatchResult(actual); - while (patternIdx < this.pattern.length && actualIdx < actual.length) { - const patternElement = this.pattern[patternIdx]; - const matcher = Matcher.isMatcher(patternElement) ? patternElement : new LiteralMatch(this.name, patternElement, { partialObjects: this.partialObjects }); - const matcherName = matcher.name; - if (this.subsequence && (matcherName == "absent" || matcherName == "anyValue")) { - throw new Error(`The Matcher ${matcherName}() cannot be nested within arrayWith()`); - } - const innerResult = matcher.test(actual[actualIdx]); - if (!this.subsequence || !innerResult.hasFailed()) { - result.compose(`[${actualIdx}]`, innerResult); - patternIdx++; - actualIdx++; - } else { - actualIdx++; - } - } - for (; patternIdx < this.pattern.length; patternIdx++) { - const pattern = this.pattern[patternIdx]; - const element = Matcher.isMatcher(pattern) || typeof pattern === "object" ? " " : ` [${pattern}] `; - result.recordFailure({ - matcher: this, - path: [], - message: `Missing element${element}at pattern index ${patternIdx}` - }); - } - return result; - } - }; - ObjectMatch = class extends Matcher { - constructor(name, pattern, options = {}) { - super(); - this.name = name; - this.pattern = pattern; - this.partial = options.partial ?? true; - } - test(actual) { - if (typeof actual !== "object" || Array.isArray(actual)) { - return new MatchResult(actual).recordFailure({ - matcher: this, - path: [], - message: `Expected type object but received ${getType(actual)}` - }); - } - const result = new MatchResult(actual); - if (!this.partial) { - for (const a of Object.keys(actual)) { - if (!(a in this.pattern)) { - result.recordFailure({ - matcher: this, - path: [`/${a}`], - message: "Unexpected key" - }); - } - } - } - for (const [patternKey, patternVal] of Object.entries(this.pattern)) { - if (!(patternKey in actual) && !(patternVal instanceof AbsentMatch)) { - result.recordFailure({ - matcher: this, - path: [`/${patternKey}`], - message: `Missing key '${patternKey}' among {${Object.keys(actual).join(",")}}` - }); - continue; - } - const matcher = Matcher.isMatcher(patternVal) ? patternVal : new LiteralMatch(this.name, patternVal, { partialObjects: this.partial }); - const inner = matcher.test(actual[patternKey]); - result.compose(`/${patternKey}`, inner); - } - return result; - } - }; - SerializedJson = class extends Matcher { - constructor(name, pattern) { - super(); - this.name = name; - this.pattern = pattern; - } - test(actual) { - const result = new MatchResult(actual); - if (getType(actual) !== "string") { - result.recordFailure({ - matcher: this, - path: [], - message: `Expected JSON as a string but found ${getType(actual)}` - }); - return result; - } - let parsed; - try { - parsed = JSON.parse(actual); - } catch (err) { - if (err instanceof SyntaxError) { - result.recordFailure({ - matcher: this, - path: [], - message: `Invalid JSON string: ${actual}` - }); - return result; - } else { - throw err; - } - } - const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); - const innerResult = matcher.test(parsed); - result.compose(`(${this.name})`, innerResult); - return result; - } - }; - NotMatch = class extends Matcher { - constructor(name, pattern) { - super(); - this.name = name; - this.pattern = pattern; - } - test(actual) { - const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); - const innerResult = matcher.test(actual); - const result = new MatchResult(actual); - if (innerResult.failCount === 0) { - result.recordFailure({ - matcher: this, - path: [], - message: `Found unexpected match: ${JSON.stringify(actual, void 0, 2)}` - }); - } - return result; - } - }; - AnyMatch = class extends Matcher { - constructor(name) { - super(); - this.name = name; - } - test(actual) { - const result = new MatchResult(actual); - if (actual == null) { - result.recordFailure({ - matcher: this, - path: [], - message: "Expected a value but found none" - }); - } - return result; - } - }; - StringLikeRegexpMatch = class extends Matcher { - constructor(name, pattern) { - super(); - this.name = name; - this.pattern = pattern; - } - test(actual) { - const result = new MatchResult(actual); - const regex = new RegExp(this.pattern, "gm"); - if (typeof actual !== "string") { - result.recordFailure({ - matcher: this, - path: [], - message: `Expected a string, but got '${typeof actual}'` - }); - } - if (!regex.test(actual)) { - result.recordFailure({ - matcher: this, - path: [], - message: `String '${actual}' did not match pattern '${this.pattern}'` - }); - } - return result; - } - }; - } -}); - -// lib/assertions/providers/lambda-handler/index.ts -var lambda_handler_exports = {}; -__export(lambda_handler_exports, { - handler: () => handler -}); -module.exports = __toCommonJS(lambda_handler_exports); - -// lib/assertions/providers/lambda-handler/base.ts -var https = __toESM(require("https")); -var url = __toESM(require("url")); -var CustomResourceHandler = class { - constructor(event, context) { - this.event = event; - this.context = context; - this.timedOut = false; - this.timeout = setTimeout(async () => { - await this.respond({ - status: "FAILED", - reason: "Lambda Function Timeout", - data: this.context.logStreamName - }); - this.timedOut = true; - }, context.getRemainingTimeInMillis() - 1200); - this.event = event; - this.physicalResourceId = extractPhysicalResourceId(event); - } - async handle() { - try { - console.log(`Event: ${JSON.stringify(this.event)}`); - const response = await this.processEvent(this.event.ResourceProperties); - console.log(`Event output : ${JSON.stringify(response)}`); - await this.respond({ - status: "SUCCESS", - reason: "OK", - data: response - }); - } catch (e) { - console.log(e); - await this.respond({ - status: "FAILED", - reason: e.message ?? "Internal Error" - }); - } finally { - clearTimeout(this.timeout); - } - } - respond(response) { - if (this.timedOut) { - return; - } - const cfResponse = { - Status: response.status, - Reason: response.reason, - PhysicalResourceId: this.physicalResourceId, - StackId: this.event.StackId, - RequestId: this.event.RequestId, - LogicalResourceId: this.event.LogicalResourceId, - NoEcho: false, - Data: response.data - }; - const responseBody = JSON.stringify(cfResponse); - console.log("Responding to CloudFormation", responseBody); - const parsedUrl = url.parse(this.event.ResponseURL); - const requestOptions = { - hostname: parsedUrl.hostname, - path: parsedUrl.path, - method: "PUT", - headers: { "content-type": "", "content-length": responseBody.length } - }; - return new Promise((resolve, reject) => { - try { - const request2 = https.request(requestOptions, resolve); - request2.on("error", reject); - request2.write(responseBody); - request2.end(); - } catch (e) { - reject(e); - } - }); - } -}; -function extractPhysicalResourceId(event) { - switch (event.RequestType) { - case "Create": - return event.LogicalResourceId; - case "Update": - case "Delete": - return event.PhysicalResourceId; - } -} - -// lib/assertions/providers/lambda-handler/assertion.ts -var { Match: Match2 } = (init_match(), __toCommonJS(match_exports)); -var AssertionHandler = class extends CustomResourceHandler { - async processEvent(request2) { - let actual = request2.actual; - const expected = decodeCall(request2.expected); - let result; - let matcher; - console.log(`Testing equality between ${JSON.stringify(request2.actual)} and ${JSON.stringify(request2.expected)}`); - switch (request2.assertionType) { - case "objectLike": - if (typeof actual === "string") { - actual = JSON.parse(actual); - } - matcher = Match2.objectLike(expected); - break; - case "equals": - matcher = Match2.exact(expected); - break; - case "arrayWith": - matcher = Match2.arrayWith(expected); - break; - default: - throw new Error(`Unsupported query type ${request2.assertionType}`); - } - const matchResult = matcher.test(actual); - matchResult.finished(); - if (matchResult.hasFailed()) { - result = { - data: JSON.stringify({ - status: "fail", - message: [ - ...matchResult.toHumanStrings(), - JSON.stringify(matchResult.target, void 0, 2) - ].join("\n") - }) - }; - } else { - result = { - data: JSON.stringify({ - status: "pass" - }) - }; - } - return result; - } -}; -function decodeCall(call) { - if (!call) { - return void 0; - } - return JSON.parse(call); -} - -// lib/assertions/providers/lambda-handler/results.ts -var ResultsCollectionHandler = class extends CustomResourceHandler { - async processEvent(request2) { - const reduced = request2.assertionResults.reduce((agg, result, idx) => { - const msg = result.status === "pass" ? "pass" : `fail - ${result.message}`; - return `${agg} -Test${idx}: ${msg}`; - }, "").trim(); - return { message: reduced }; - } -}; - -// lib/assertions/providers/lambda-handler/utils.ts -function decode(object) { - return JSON.parse(JSON.stringify(object), (_k, v) => { - switch (v) { - case "TRUE:BOOLEAN": - return true; - case "FALSE:BOOLEAN": - return false; - case !isNaN(+v): - return +v; - default: - return v; - } - }); -} - -// lib/assertions/providers/lambda-handler/sdk.ts -function flatten(object) { - return Object.assign({}, ...function _flatten(child, path = []) { - return [].concat(...Object.keys(child).map((key) => { - const childKey = Buffer.isBuffer(child[key]) ? child[key].toString("utf8") : child[key]; - return typeof childKey === "object" && childKey !== null ? _flatten(childKey, path.concat([key])) : { [path.concat([key]).join(".")]: childKey }; - })); - }(object)); -} -var SdkHandler = class extends CustomResourceHandler { - async processEvent(request2) { - const AWS = require("aws-sdk"); - console.log(`AWS SDK VERSION: ${AWS.VERSION}`); - const service = new AWS[request2.service](); - const response = await service[request2.api](request2.parameters && decode(request2.parameters)).promise(); - console.log(`SDK response received ${JSON.stringify(response)}`); - delete response.ResponseMetadata; - const respond = { - apiCallResponse: response - }; - const flatData = __spreadValues({}, flatten(respond)); - return request2.flattenResponse === "true" ? flatData : respond; - } -}; - -// lib/assertions/providers/lambda-handler/types.ts -var ASSERT_RESOURCE_TYPE = "Custom::DeployAssert@AssertEquals"; -var RESULTS_RESOURCE_TYPE = "Custom::DeployAssert@ResultsCollection"; -var SDK_RESOURCE_TYPE_PREFIX = "Custom::DeployAssert@SdkCall"; - -// lib/assertions/providers/lambda-handler/index.ts -async function handler(event, context) { - const provider = createResourceHandler(event, context); - await provider.handle(); -} -function createResourceHandler(event, context) { - if (event.ResourceType.startsWith(SDK_RESOURCE_TYPE_PREFIX)) { - return new SdkHandler(event, context); - } - switch (event.ResourceType) { - case ASSERT_RESOURCE_TYPE: - return new AssertionHandler(event, context); - case RESULTS_RESOURCE_TYPE: - return new ResultsCollectionHandler(event, context); - default: - throw new Error(`Unsupported resource type "${event.ResourceType}`); - } -} -// Annotate the CommonJS export names for ESM import in node: -0 && (module.exports = { - handler -}); diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/aws-cdk-lambda-destinations.template.json b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/aws-cdk-lambda-destinations.template.json index 87d704ed4327a..84e4ea60c1c0e 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/aws-cdk-lambda-destinations.template.json +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/aws-cdk-lambda-destinations.template.json @@ -80,7 +80,7 @@ "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "ZipFile": "exports.handler = async (event) => {\n if (event === 'OK') return 'success';\n throw new Error('failure');\n };" + "ZipFile": "exports.handler = async (event) => {\n if (event.status === 'OK') return 'success';\n throw new Error('failure');\n };" }, "Role": { "Fn::GetAtt": [ @@ -281,7 +281,7 @@ "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "ZipFile": "exports.handler = async (event) => {\n if (event === 'OK') return 'success';\n throw new Error('failure');\n };" + "ZipFile": "exports.handler = async (event) => {\n if (event.status === 'OK') return 'success';\n throw new Error('failure');\n };" }, "Role": { "Fn::GetAtt": [ @@ -391,5 +391,23 @@ "MaximumRetryAttempts": 0 } } + }, + "Outputs": { + "ExportsOutputRefSnsSqsC4810B27404A5AFF": { + "Value": { + "Ref": "SnsSqsC4810B27" + }, + "Export": { + "Name": "aws-cdk-lambda-destinations:ExportsOutputRefSnsSqsC4810B27404A5AFF" + } + }, + "ExportsOutputRefQueue4A7E3555425E8BD3": { + "Value": { + "Ref": "Queue4A7E3555" + }, + "Export": { + "Name": "aws-cdk-lambda-destinations:ExportsOutputRefQueue4A7E3555425E8BD3" + } + } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/cdk.out index 90bef2e09ad39..ccdfc1ff96a9d 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"17.0.0"} \ No newline at end of file +{"version":"19.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/integ.json b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/integ.json index 20b73bceabb1f..96acfc50bc0cf 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/integ.json +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/integ.json @@ -1,14 +1,11 @@ { - "version": "18.0.0", + "version": "19.0.0", "testCases": { - "aws-lambda-destinations/test/integ.destinations": { + "Destinations/DefaultTest": { "stacks": [ "aws-cdk-lambda-destinations" ], - "diffAssets": false, - "stackUpdateWorkflow": true + "assertionStack": "DestinationsDefaultTestDeployAssertCC49E667" } - }, - "synthContext": {}, - "enableLookups": false + } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/manifest.json index 2684d0546624f..87df11e84970f 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "17.0.0", + "version": "19.0.0", "artifacts": { "Tree": { "type": "cdk:tree", @@ -104,9 +104,109 @@ "type": "aws:cdk:logicalId", "data": "MySpecialAliasEventInvokeConfig05FF4E2F" } + ], + "/aws-cdk-lambda-destinations/Exports/Output{\"Ref\":\"SnsSqsC4810B27\"}": [ + { + "type": "aws:cdk:logicalId", + "data": "ExportsOutputRefSnsSqsC4810B27404A5AFF" + } + ], + "/aws-cdk-lambda-destinations/Exports/Output{\"Ref\":\"Queue4A7E3555\"}": [ + { + "type": "aws:cdk:logicalId", + "data": "ExportsOutputRefQueue4A7E3555425E8BD3" + } ] }, "displayName": "aws-cdk-lambda-destinations" + }, + "DestinationsDefaultTestDeployAssertCC49E667": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "DestinationsDefaultTestDeployAssertCC49E667.template.json", + "validateOnSynth": false + }, + "dependencies": [ + "aws-cdk-lambda-destinations" + ], + "metadata": { + "/Destinations/DefaultTest/DeployAssert": [ + { + "type": "aws:cdk:asset", + "data": { + "path": "asset.1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b.bundle", + "id": "1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b", + "packaging": "zip", + "sourceHash": "1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b", + "s3BucketParameter": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3BucketF7210344", + "s3KeyParameter": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3VersionKey1E71961C", + "artifactHashParameter": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bArtifactHash4F8362F2" + } + } + ], + "/Destinations/DefaultTest/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "LambdaInvoked12df417a1b74909abb3ea643735a310" + } + ], + "/Destinations/DefaultTest/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/Invoke": [ + { + "type": "aws:cdk:logicalId", + "data": "LambdaInvoked12df417a1b74909abb3ea643735a310InvokeF590C289" + } + ], + "/Destinations/DefaultTest/DeployAssert/Default/AwsApiCallSQSreceiveMessage/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "AwsApiCallSQSreceiveMessage" + } + ], + "/Destinations/DefaultTest/DeployAssert/Default/AwsApiCallSQSreceiveMessage/AssertEqualsSQSreceiveMessage/Default/Default": [ + { + "type": "aws:cdk:logicalId", + "data": "AwsApiCallSQSreceiveMessageAssertEqualsSQSreceiveMessage56120636" + } + ], + "/Destinations/DefaultTest/DeployAssert/Default/AwsApiCallSQSreceiveMessage/AssertEqualsSQSreceiveMessage/AssertionResults": [ + { + "type": "aws:cdk:logicalId", + "data": "AssertionResultsAssertEqualsSQSreceiveMessage" + } + ], + "/Destinations/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73" + } + ], + "/Destinations/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler": [ + { + "type": "aws:cdk:logicalId", + "data": "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F" + } + ], + "/Destinations/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b/S3Bucket": [ + { + "type": "aws:cdk:logicalId", + "data": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3BucketF7210344" + } + ], + "/Destinations/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b/S3VersionKey": [ + { + "type": "aws:cdk:logicalId", + "data": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3VersionKey1E71961C" + } + ], + "/Destinations/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b/ArtifactHash": [ + { + "type": "aws:cdk:logicalId", + "data": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bArtifactHash4F8362F2" + } + ] + }, + "displayName": "Destinations/DefaultTest/DeployAssert" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/tree.json b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/tree.json index 60299ae021368..21deb427e0113 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-lambda-destinations/test/destinations.integ.snapshot/tree.json @@ -175,7 +175,7 @@ "aws:cdk:cloudformation:type": "AWS::Lambda::Function", "aws:cdk:cloudformation:props": { "code": { - "zipFile": "exports.handler = async (event) => {\n if (event === 'OK') return 'success';\n throw new Error('failure');\n };" + "zipFile": "exports.handler = async (event) => {\n if (event.status === 'OK') return 'success';\n throw new Error('failure');\n };" }, "role": { "Fn::GetAtt": [ @@ -495,7 +495,7 @@ "aws:cdk:cloudformation:type": "AWS::Lambda::Function", "aws:cdk:cloudformation:props": { "code": { - "zipFile": "exports.handler = async (event) => {\n if (event === 'OK') return 'success';\n throw new Error('failure');\n };" + "zipFile": "exports.handler = async (event) => {\n if (event.status === 'OK') return 'success';\n throw new Error('failure');\n };" }, "role": { "Fn::GetAtt": [ @@ -681,12 +681,310 @@ "fqn": "@aws-cdk/aws-lambda.Alias", "version": "0.0.0" } + }, + "Exports": { + "id": "Exports", + "path": "aws-cdk-lambda-destinations/Exports", + "children": { + "Output{\"Ref\":\"SnsSqsC4810B27\"}": { + "id": "Output{\"Ref\":\"SnsSqsC4810B27\"}", + "path": "aws-cdk-lambda-destinations/Exports/Output{\"Ref\":\"SnsSqsC4810B27\"}", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + }, + "Output{\"Ref\":\"Queue4A7E3555\"}": { + "id": "Output{\"Ref\":\"Queue4A7E3555\"}", + "path": "aws-cdk-lambda-destinations/Exports/Output{\"Ref\":\"Queue4A7E3555\"}", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } } }, "constructInfo": { "fqn": "@aws-cdk/core.Stack", "version": "0.0.0" } + }, + "Destinations": { + "id": "Destinations", + "path": "Destinations", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "Destinations/DefaultTest", + "children": { + "DeployAssert": { + "id": "DeployAssert", + "path": "Destinations/DefaultTest/DeployAssert", + "children": { + "Default": { + "id": "Default", + "path": "Destinations/DefaultTest/DeployAssert/Default", + "children": { + "LambdaInvoked12df417a1b74909abb3ea643735a310": { + "id": "LambdaInvoked12df417a1b74909abb3ea643735a310", + "path": "Destinations/DefaultTest/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310", + "children": { + "SdkProvider": { + "id": "SdkProvider", + "path": "Destinations/DefaultTest/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/SdkProvider", + "children": { + "AssertionsProvider": { + "id": "AssertionsProvider", + "path": "Destinations/DefaultTest/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/SdkProvider/AssertionsProvider", + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AssertionsProvider", + "version": "0.0.0" + } + }, + "Default": { + "id": "Default", + "path": "Destinations/DefaultTest/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/Default", + "children": { + "Default": { + "id": "Default", + "path": "Destinations/DefaultTest/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/Default/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + }, + "Invoke": { + "id": "Invoke", + "path": "Destinations/DefaultTest/DeployAssert/Default/LambdaInvoked12df417a1b74909abb3ea643735a310/Invoke", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.LambdaInvokeFunction", + "version": "0.0.0" + } + }, + "AwsApiCallSQSreceiveMessage": { + "id": "AwsApiCallSQSreceiveMessage", + "path": "Destinations/DefaultTest/DeployAssert/Default/AwsApiCallSQSreceiveMessage", + "children": { + "SdkProvider": { + "id": "SdkProvider", + "path": "Destinations/DefaultTest/DeployAssert/Default/AwsApiCallSQSreceiveMessage/SdkProvider", + "children": { + "AssertionsProvider": { + "id": "AssertionsProvider", + "path": "Destinations/DefaultTest/DeployAssert/Default/AwsApiCallSQSreceiveMessage/SdkProvider/AssertionsProvider", + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AssertionsProvider", + "version": "0.0.0" + } + }, + "Default": { + "id": "Default", + "path": "Destinations/DefaultTest/DeployAssert/Default/AwsApiCallSQSreceiveMessage/Default", + "children": { + "Default": { + "id": "Default", + "path": "Destinations/DefaultTest/DeployAssert/Default/AwsApiCallSQSreceiveMessage/Default/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + }, + "AssertEqualsSQSreceiveMessage": { + "id": "AssertEqualsSQSreceiveMessage", + "path": "Destinations/DefaultTest/DeployAssert/Default/AwsApiCallSQSreceiveMessage/AssertEqualsSQSreceiveMessage", + "children": { + "AssertionProvider": { + "id": "AssertionProvider", + "path": "Destinations/DefaultTest/DeployAssert/Default/AwsApiCallSQSreceiveMessage/AssertEqualsSQSreceiveMessage/AssertionProvider", + "children": { + "AssertionsProvider": { + "id": "AssertionsProvider", + "path": "Destinations/DefaultTest/DeployAssert/Default/AwsApiCallSQSreceiveMessage/AssertEqualsSQSreceiveMessage/AssertionProvider/AssertionsProvider", + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AssertionsProvider", + "version": "0.0.0" + } + }, + "Default": { + "id": "Default", + "path": "Destinations/DefaultTest/DeployAssert/Default/AwsApiCallSQSreceiveMessage/AssertEqualsSQSreceiveMessage/Default", + "children": { + "Default": { + "id": "Default", + "path": "Destinations/DefaultTest/DeployAssert/Default/AwsApiCallSQSreceiveMessage/AssertEqualsSQSreceiveMessage/Default/Default", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.CustomResource", + "version": "0.0.0" + } + }, + "AssertionResults": { + "id": "AssertionResults", + "path": "Destinations/DefaultTest/DeployAssert/Default/AwsApiCallSQSreceiveMessage/AssertEqualsSQSreceiveMessage/AssertionResults", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.EqualsAssertion", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.AwsApiCall", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.DeployAssert", + "version": "0.0.0" + } + }, + "SingletonFunction1488541a7b23466481b69b4408076b81": { + "id": "SingletonFunction1488541a7b23466481b69b4408076b81", + "path": "Destinations/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81", + "children": { + "Staging": { + "id": "Staging", + "path": "Destinations/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Staging", + "constructInfo": { + "fqn": "@aws-cdk/core.AssetStaging", + "version": "0.0.0" + } + }, + "Role": { + "id": "Role", + "path": "Destinations/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + }, + "Handler": { + "id": "Handler", + "path": "Destinations/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnResource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + }, + "AssetParameters": { + "id": "AssetParameters", + "path": "Destinations/DefaultTest/DeployAssert/AssetParameters", + "children": { + "1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b": { + "id": "1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b", + "path": "Destinations/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b", + "children": { + "S3Bucket": { + "id": "S3Bucket", + "path": "Destinations/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b/S3Bucket", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "S3VersionKey": { + "id": "S3VersionKey", + "path": "Destinations/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b/S3VersionKey", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "ArtifactHash": { + "id": "ArtifactHash", + "path": "Destinations/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b/ArtifactHash", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Construct", + "version": "0.0.0" + } } }, "constructInfo": { diff --git a/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts b/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts index 2408222d67827..61009f180e53d 100644 --- a/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts +++ b/packages/@aws-cdk/aws-lambda-destinations/test/integ.destinations.ts @@ -69,7 +69,7 @@ class TestStack extends Stack { const app = new App(); const stack = new TestStack(app, 'aws-cdk-lambda-destinations'); -const integ = new IntegTest(app, 'Integ', { +const integ = new IntegTest(app, 'Destinations', { testCases: [stack], }); diff --git a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/IntegTestDefaultTestCaseDeployAssert4F885E41.template.json b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/BundlingDefaultTestDeployAssertAACA0CAF.template.json similarity index 74% rename from packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/IntegTestDefaultTestCaseDeployAssert4F885E41.template.json rename to packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/BundlingDefaultTestDeployAssertAACA0CAF.template.json index 0487b84a86aad..f1587148e1a58 100644 --- a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/IntegTestDefaultTestCaseDeployAssert4F885E41.template.json +++ b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/BundlingDefaultTestDeployAssertAACA0CAF.template.json @@ -17,7 +17,7 @@ } }, "flattenResponse": "false", - "salt": "1651497936527" + "salt": "1651691789905" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -37,7 +37,7 @@ } } }, - "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8EqualsAssertionLambdainvoke4FA91B10": { + "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8AssertEqualsLambdainvoke89C63F4A": { "Type": "Custom::DeployAssert@AssertEquals", "Properties": { "ServiceToken": { @@ -52,9 +52,8 @@ "apiCallResponse" ] }, - "expected": "{\"Payload\":\"200\"}", - "assertionType": "objectLike", - "salt": "1651497936528" + "expected": "{\"$ObjectLike\":{\"Payload\":\"200\"}}", + "salt": "1651691789906" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -137,7 +136,7 @@ "Runtime": "nodejs14.x", "Code": { "S3Bucket": { - "Ref": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB" + "Ref": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3BucketF7210344" }, "S3Key": { "Fn::Join": [ @@ -150,7 +149,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4" + "Ref": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3VersionKey1E71961C" } ] } @@ -163,7 +162,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4" + "Ref": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3VersionKey1E71961C" } ] } @@ -185,27 +184,27 @@ } }, "Outputs": { - "AssertionResultsEqualsAssertionLambdainvoke": { + "AssertionResultsAssertEqualsLambdainvoke": { "Value": { "Fn::GetAtt": [ - "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8EqualsAssertionLambdainvoke4FA91B10", + "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8AssertEqualsLambdainvoke89C63F4A", "data" ] } } }, "Parameters": { - "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB": { + "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3BucketF7210344": { "Type": "String", - "Description": "S3 bucket for asset \"37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0\"" + "Description": "S3 bucket for asset \"1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b\"" }, - "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4": { + "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3VersionKey1E71961C": { "Type": "String", - "Description": "S3 key for asset version \"37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0\"" + "Description": "S3 key for asset version \"1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b\"" }, - "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0ArtifactHash4C05AFBC": { + "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bArtifactHash4F8362F2": { "Type": "String", - "Description": "Artifact hash for asset \"37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0\"" + "Description": "Artifact hash for asset \"1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/cdk.out index 2efc89439fab8..ccdfc1ff96a9d 100644 --- a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"18.0.0"} \ No newline at end of file +{"version":"19.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/integ.json b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/integ.json index 02690474d95f4..713bce6bb246e 100644 --- a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/integ.json +++ b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/integ.json @@ -1,12 +1,12 @@ { - "version": "18.0.0", + "version": "19.0.0", "testCases": { - "DefaultTestCase": { + "Bundling/DefaultTest": { "stacks": [ "cdk-integ-lambda-bundling" ], "stackUpdateWorkflow": false, - "assertionStack": "IntegTestDefaultTestCaseDeployAssert4F885E41" + "assertionStack": "BundlingDefaultTestDeployAssertAACA0CAF" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/manifest.json index c881060526e59..d49cacb755c4f 100644 --- a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "18.0.0", + "version": "19.0.0", "artifacts": { "Tree": { "type": "cdk:tree", @@ -68,87 +68,87 @@ }, "displayName": "cdk-integ-lambda-bundling" }, - "IntegTestDefaultTestCaseDeployAssert4F885E41": { + "BundlingDefaultTestDeployAssertAACA0CAF": { "type": "aws:cloudformation:stack", "environment": "aws://unknown-account/unknown-region", "properties": { - "templateFile": "IntegTestDefaultTestCaseDeployAssert4F885E41.template.json", + "templateFile": "BundlingDefaultTestDeployAssertAACA0CAF.template.json", "validateOnSynth": false }, "dependencies": [ "cdk-integ-lambda-bundling" ], "metadata": { - "/IntegTest/DefaultTestCase/DeployAssert": [ + "/Bundling/DefaultTest/DeployAssert": [ { "type": "aws:cdk:asset", "data": { - "path": "asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle", - "id": "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", + "path": "asset.1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b.bundle", + "id": "1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b", "packaging": "zip", - "sourceHash": "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", - "s3BucketParameter": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB", - "s3KeyParameter": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4", - "artifactHashParameter": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0ArtifactHash4C05AFBC" + "sourceHash": "1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b", + "s3BucketParameter": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3BucketF7210344", + "s3KeyParameter": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3VersionKey1E71961C", + "artifactHashParameter": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bArtifactHash4F8362F2" } } ], - "/IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/Default/Default": [ + "/Bundling/DefaultTest/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/Default/Default": [ { "type": "aws:cdk:logicalId", "data": "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8" } ], - "/IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/Invoke": [ + "/Bundling/DefaultTest/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/Invoke": [ { "type": "aws:cdk:logicalId", "data": "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8InvokeA3F6E40A" } ], - "/IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/EqualsAssertionLambdainvoke/Default/Default": [ + "/Bundling/DefaultTest/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/AssertEqualsLambdainvoke/Default/Default": [ { "type": "aws:cdk:logicalId", - "data": "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8EqualsAssertionLambdainvoke4FA91B10" + "data": "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8AssertEqualsLambdainvoke89C63F4A" } ], - "/IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/EqualsAssertionLambdainvoke/AssertionResults": [ + "/Bundling/DefaultTest/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/AssertEqualsLambdainvoke/AssertionResults": [ { "type": "aws:cdk:logicalId", - "data": "AssertionResultsEqualsAssertionLambdainvoke" + "data": "AssertionResultsAssertEqualsLambdainvoke" } ], - "/IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role": [ + "/Bundling/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role": [ { "type": "aws:cdk:logicalId", "data": "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73" } ], - "/IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler": [ + "/Bundling/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler": [ { "type": "aws:cdk:logicalId", "data": "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F" } ], - "/IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3Bucket": [ + "/Bundling/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b/S3Bucket": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB" + "data": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3BucketF7210344" } ], - "/IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3VersionKey": [ + "/Bundling/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b/S3VersionKey": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4" + "data": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3VersionKey1E71961C" } ], - "/IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/ArtifactHash": [ + "/Bundling/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b/ArtifactHash": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0ArtifactHash4C05AFBC" + "data": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bArtifactHash4F8362F2" } ] }, - "displayName": "IntegTest/DefaultTestCase/DeployAssert" + "displayName": "Bundling/DefaultTest/DeployAssert" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/tree.json b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/tree.json index ffd7f7cd73615..55062d01c72fa 100644 --- a/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-lambda/test/bundling.integ.snapshot/tree.json @@ -228,33 +228,33 @@ "version": "0.0.0" } }, - "IntegTest": { - "id": "IntegTest", - "path": "IntegTest", + "Bundling": { + "id": "Bundling", + "path": "Bundling", "children": { - "DefaultTestCase": { - "id": "DefaultTestCase", - "path": "IntegTest/DefaultTestCase", + "DefaultTest": { + "id": "DefaultTest", + "path": "Bundling/DefaultTest", "children": { "DeployAssert": { "id": "DeployAssert", - "path": "IntegTest/DefaultTestCase/DeployAssert", + "path": "Bundling/DefaultTest/DeployAssert", "children": { "Default": { "id": "Default", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default", + "path": "Bundling/DefaultTest/DeployAssert/Default", "children": { "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8": { "id": "LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8", + "path": "Bundling/DefaultTest/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8", "children": { "SdkProvider": { "id": "SdkProvider", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/SdkProvider", + "path": "Bundling/DefaultTest/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/SdkProvider", "children": { "AssertionsProvider": { "id": "AssertionsProvider", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/SdkProvider/AssertionsProvider", + "path": "Bundling/DefaultTest/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/SdkProvider/AssertionsProvider", "constructInfo": { "fqn": "@aws-cdk/core.Construct", "version": "0.0.0" @@ -268,11 +268,11 @@ }, "Default": { "id": "Default", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/Default", + "path": "Bundling/DefaultTest/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/Default", "children": { "Default": { "id": "Default", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/Default/Default", + "path": "Bundling/DefaultTest/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/Default/Default", "constructInfo": { "fqn": "@aws-cdk/core.CfnResource", "version": "0.0.0" @@ -286,23 +286,23 @@ }, "Invoke": { "id": "Invoke", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/Invoke", + "path": "Bundling/DefaultTest/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/Invoke", "constructInfo": { "fqn": "@aws-cdk/core.CfnResource", "version": "0.0.0" } }, - "EqualsAssertionLambdainvoke": { - "id": "EqualsAssertionLambdainvoke", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/EqualsAssertionLambdainvoke", + "AssertEqualsLambdainvoke": { + "id": "AssertEqualsLambdainvoke", + "path": "Bundling/DefaultTest/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/AssertEqualsLambdainvoke", "children": { "AssertionProvider": { "id": "AssertionProvider", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/EqualsAssertionLambdainvoke/AssertionProvider", + "path": "Bundling/DefaultTest/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/AssertEqualsLambdainvoke/AssertionProvider", "children": { "AssertionsProvider": { "id": "AssertionsProvider", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/EqualsAssertionLambdainvoke/AssertionProvider/AssertionsProvider", + "path": "Bundling/DefaultTest/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/AssertEqualsLambdainvoke/AssertionProvider/AssertionsProvider", "constructInfo": { "fqn": "@aws-cdk/core.Construct", "version": "0.0.0" @@ -316,11 +316,11 @@ }, "Default": { "id": "Default", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/EqualsAssertionLambdainvoke/Default", + "path": "Bundling/DefaultTest/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/AssertEqualsLambdainvoke/Default", "children": { "Default": { "id": "Default", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/EqualsAssertionLambdainvoke/Default/Default", + "path": "Bundling/DefaultTest/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/AssertEqualsLambdainvoke/Default/Default", "constructInfo": { "fqn": "@aws-cdk/core.CfnResource", "version": "0.0.0" @@ -334,7 +334,7 @@ }, "AssertionResults": { "id": "AssertionResults", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/EqualsAssertionLambdainvoke/AssertionResults", + "path": "Bundling/DefaultTest/DeployAssert/Default/LambdaInvoke55933c6da447c7ea94ebd3a50e8557a8/AssertEqualsLambdainvoke/AssertionResults", "constructInfo": { "fqn": "@aws-cdk/core.CfnOutput", "version": "0.0.0" @@ -360,11 +360,11 @@ }, "SingletonFunction1488541a7b23466481b69b4408076b81": { "id": "SingletonFunction1488541a7b23466481b69b4408076b81", - "path": "IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81", + "path": "Bundling/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81", "children": { "Staging": { "id": "Staging", - "path": "IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Staging", + "path": "Bundling/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Staging", "constructInfo": { "fqn": "@aws-cdk/core.AssetStaging", "version": "0.0.0" @@ -372,7 +372,7 @@ }, "Role": { "id": "Role", - "path": "IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role", + "path": "Bundling/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role", "constructInfo": { "fqn": "@aws-cdk/core.CfnResource", "version": "0.0.0" @@ -380,7 +380,7 @@ }, "Handler": { "id": "Handler", - "path": "IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler", + "path": "Bundling/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler", "constructInfo": { "fqn": "@aws-cdk/core.CfnResource", "version": "0.0.0" @@ -394,15 +394,15 @@ }, "AssetParameters": { "id": "AssetParameters", - "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters", + "path": "Bundling/DefaultTest/DeployAssert/AssetParameters", "children": { - "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0": { - "id": "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", - "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", + "1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b": { + "id": "1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b", + "path": "Bundling/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b", "children": { "S3Bucket": { "id": "S3Bucket", - "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3Bucket", + "path": "Bundling/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b/S3Bucket", "constructInfo": { "fqn": "@aws-cdk/core.CfnParameter", "version": "0.0.0" @@ -410,7 +410,7 @@ }, "S3VersionKey": { "id": "S3VersionKey", - "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3VersionKey", + "path": "Bundling/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b/S3VersionKey", "constructInfo": { "fqn": "@aws-cdk/core.CfnParameter", "version": "0.0.0" @@ -418,7 +418,7 @@ }, "ArtifactHash": { "id": "ArtifactHash", - "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/ArtifactHash", + "path": "Bundling/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b/ArtifactHash", "constructInfo": { "fqn": "@aws-cdk/core.CfnParameter", "version": "0.0.0" @@ -444,13 +444,13 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "fqn": "@aws-cdk/core.Construct", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/integ-tests.IntegTest", + "fqn": "@aws-cdk/core.Construct", "version": "0.0.0" } } diff --git a/packages/@aws-cdk/aws-lambda/test/integ.bundling.ts b/packages/@aws-cdk/aws-lambda/test/integ.bundling.ts index d13c18646a611..30e28c8ef5fe7 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.bundling.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.bundling.ts @@ -42,7 +42,7 @@ class TestStack extends Stack { const app = new App(); const stack = new TestStack(app, 'cdk-integ-lambda-bundling'); -const integ = new IntegTest(app, 'IntegTest', { +const integ = new IntegTest(app, 'Bundling', { testCases: [stack], stackUpdateWorkflow: false, }); diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/integ.put-events.ts b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/integ.put-events.ts index 6f0b9f83a1f82..1c6ee34a3341b 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/integ.put-events.ts +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/integ.put-events.ts @@ -41,7 +41,7 @@ const sm = new sfn.StateMachine(stack, 'StateMachine', { }); -const testCase = new IntegTest(app, 'IntegTest', { +const testCase = new IntegTest(app, 'PutEvents', { testCases: [stack], }); diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/IntegTestDefaultTestCaseDeployAssert4F885E41.template.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/PutEventsDefaultTestDeployAssert1A6BA3F3.template.json similarity index 68% rename from packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/IntegTestDefaultTestCaseDeployAssert4F885E41.template.json rename to packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/PutEventsDefaultTestDeployAssert1A6BA3F3.template.json index f130c19d1729d..d8ba1b9c2554d 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/IntegTestDefaultTestCaseDeployAssert4F885E41.template.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/PutEventsDefaultTestDeployAssert1A6BA3F3.template.json @@ -1,6 +1,6 @@ { "Resources": { - "SdkQueryStepFunctionsstartExecution": { + "AwsApiCallStepFunctionsstartExecution": { "Type": "Custom::DeployAssert@SdkCallStepFunctionsstartExecution", "Properties": { "ServiceToken": { @@ -17,12 +17,12 @@ } }, "flattenResponse": "true", - "salt": "1651498056105" + "salt": "1651691787968" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "SdkQueryStepFunctionsdescribeExecution": { + "AwsApiCallStepFunctionsdescribeExecution": { "Type": "Custom::DeployAssert@SdkCallStepFunctionsdescribeExecution", "Properties": { "ServiceToken": { @@ -36,18 +36,18 @@ "parameters": { "executionArn": { "Fn::GetAtt": [ - "SdkQueryStepFunctionsstartExecution", + "AwsApiCallStepFunctionsstartExecution", "apiCallResponse.executionArn" ] } }, "flattenResponse": "false", - "salt": "1651498056106" + "salt": "1651691787969" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" }, - "SdkQueryStepFunctionsdescribeExecutionEqualsAssertionStepFunctionsdescribeExecutionA4C8D5D7": { + "AwsApiCallStepFunctionsdescribeExecutionAssertEqualsStepFunctionsdescribeExecution58E75A69": { "Type": "Custom::DeployAssert@AssertEquals", "Properties": { "ServiceToken": { @@ -58,13 +58,12 @@ }, "actual": { "Fn::GetAtt": [ - "SdkQueryStepFunctionsdescribeExecution", + "AwsApiCallStepFunctionsdescribeExecution", "apiCallResponse" ] }, - "expected": "{\"status\":\"SUCCEEDED\"}", - "assertionType": "objectLike", - "salt": "1651498056107" + "expected": "{\"$ObjectLike\":{\"status\":\"SUCCEEDED\"}}", + "salt": "1651691787970" }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete" @@ -125,7 +124,7 @@ "Runtime": "nodejs14.x", "Code": { "S3Bucket": { - "Ref": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB" + "Ref": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3BucketF7210344" }, "S3Key": { "Fn::Join": [ @@ -138,7 +137,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4" + "Ref": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3VersionKey1E71961C" } ] } @@ -151,7 +150,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4" + "Ref": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3VersionKey1E71961C" } ] } @@ -173,27 +172,27 @@ } }, "Outputs": { - "AssertionResultsEqualsAssertionStepFunctionsdescribeExecution": { + "AssertionResultsAssertEqualsStepFunctionsdescribeExecution": { "Value": { "Fn::GetAtt": [ - "SdkQueryStepFunctionsdescribeExecutionEqualsAssertionStepFunctionsdescribeExecutionA4C8D5D7", + "AwsApiCallStepFunctionsdescribeExecutionAssertEqualsStepFunctionsdescribeExecution58E75A69", "data" ] } } }, "Parameters": { - "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB": { + "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3BucketF7210344": { "Type": "String", - "Description": "S3 bucket for asset \"37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0\"" + "Description": "S3 bucket for asset \"1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b\"" }, - "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4": { + "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3VersionKey1E71961C": { "Type": "String", - "Description": "S3 key for asset version \"37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0\"" + "Description": "S3 key for asset version \"1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b\"" }, - "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0ArtifactHash4C05AFBC": { + "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bArtifactHash4F8362F2": { "Type": "String", - "Description": "Artifact hash for asset \"37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0\"" + "Description": "Artifact hash for asset \"1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/asset.1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b.bundle/index.js b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/asset.1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b.bundle/index.js new file mode 100644 index 0000000000000..32e3e2c1e5a95 --- /dev/null +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/asset.1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b.bundle/index.js @@ -0,0 +1,644 @@ +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getOwnPropSymbols = Object.getOwnPropertySymbols; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __propIsEnum = Object.prototype.propertyIsEnumerable; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __spreadValues = (a, b) => { + for (var prop in b || (b = {})) + if (__hasOwnProp.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + if (__getOwnPropSymbols) + for (var prop of __getOwnPropSymbols(b)) { + if (__propIsEnum.call(b, prop)) + __defNormalProp(a, prop, b[prop]); + } + return a; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// lib/assertions/providers/lambda-handler/index.ts +var lambda_handler_exports = {}; +__export(lambda_handler_exports, { + handler: () => handler +}); +module.exports = __toCommonJS(lambda_handler_exports); + +// ../assertions/lib/matcher.ts +var Matcher = class { + static isMatcher(x) { + return x && x instanceof Matcher; + } +}; +var MatchResult = class { + constructor(target) { + this.failures = []; + this.captures = /* @__PURE__ */ new Map(); + this.finalized = false; + this.target = target; + } + push(matcher, path, message) { + return this.recordFailure({ matcher, path, message }); + } + recordFailure(failure) { + this.failures.push(failure); + return this; + } + hasFailed() { + return this.failures.length !== 0; + } + get failCount() { + return this.failures.length; + } + compose(id, inner) { + const innerF = inner.failures; + this.failures.push(...innerF.map((f) => { + return { path: [id, ...f.path], message: f.message, matcher: f.matcher }; + })); + inner.captures.forEach((vals, capture) => { + vals.forEach((value) => this.recordCapture({ capture, value })); + }); + return this; + } + finished() { + if (this.finalized) { + return this; + } + if (this.failCount === 0) { + this.captures.forEach((vals, cap) => cap._captured.push(...vals)); + } + this.finalized = true; + return this; + } + toHumanStrings() { + return this.failures.map((r) => { + const loc = r.path.length === 0 ? "" : ` at ${r.path.join("")}`; + return "" + r.message + loc + ` (using ${r.matcher.name} matcher)`; + }); + } + recordCapture(options) { + let values = this.captures.get(options.capture); + if (values === void 0) { + values = []; + } + values.push(options.value); + this.captures.set(options.capture, values); + } +}; + +// ../assertions/lib/private/matchers/absent.ts +var AbsentMatch = class extends Matcher { + constructor(name) { + super(); + this.name = name; + } + test(actual) { + const result = new MatchResult(actual); + if (actual !== void 0) { + result.recordFailure({ + matcher: this, + path: [], + message: `Received ${actual}, but key should be absent` + }); + } + return result; + } +}; + +// ../assertions/lib/private/type.ts +function getType(obj) { + return Array.isArray(obj) ? "array" : typeof obj; +} + +// ../assertions/lib/match.ts +var Match = class { + static absent() { + return new AbsentMatch("absent"); + } + static arrayWith(pattern) { + return new ArrayMatch("arrayWith", pattern); + } + static arrayEquals(pattern) { + return new ArrayMatch("arrayEquals", pattern, { subsequence: false }); + } + static exact(pattern) { + return new LiteralMatch("exact", pattern, { partialObjects: false }); + } + static objectLike(pattern) { + return new ObjectMatch("objectLike", pattern); + } + static objectEquals(pattern) { + return new ObjectMatch("objectEquals", pattern, { partial: false }); + } + static not(pattern) { + return new NotMatch("not", pattern); + } + static serializedJson(pattern) { + return new SerializedJson("serializedJson", pattern); + } + static anyValue() { + return new AnyMatch("anyValue"); + } + static stringLikeRegexp(pattern) { + return new StringLikeRegexpMatch("stringLikeRegexp", pattern); + } +}; +var LiteralMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.partialObjects = options.partialObjects ?? false; + if (Matcher.isMatcher(this.pattern)) { + throw new Error("LiteralMatch cannot directly contain another matcher. Remove the top-level matcher or nest it more deeply."); + } + } + test(actual) { + if (Array.isArray(this.pattern)) { + return new ArrayMatch(this.name, this.pattern, { subsequence: false, partialObjects: this.partialObjects }).test(actual); + } + if (typeof this.pattern === "object") { + return new ObjectMatch(this.name, this.pattern, { partial: this.partialObjects }).test(actual); + } + const result = new MatchResult(actual); + if (typeof this.pattern !== typeof actual) { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected type ${typeof this.pattern} but received ${getType(actual)}` + }); + return result; + } + if (actual !== this.pattern) { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected ${this.pattern} but received ${actual}` + }); + } + return result; + } +}; +var ArrayMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.subsequence = options.subsequence ?? true; + this.partialObjects = options.partialObjects ?? false; + } + test(actual) { + if (!Array.isArray(actual)) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected type array but received ${getType(actual)}` + }); + } + if (!this.subsequence && this.pattern.length !== actual.length) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected array of length ${this.pattern.length} but received ${actual.length}` + }); + } + let patternIdx = 0; + let actualIdx = 0; + const result = new MatchResult(actual); + while (patternIdx < this.pattern.length && actualIdx < actual.length) { + const patternElement = this.pattern[patternIdx]; + const matcher = Matcher.isMatcher(patternElement) ? patternElement : new LiteralMatch(this.name, patternElement, { partialObjects: this.partialObjects }); + const matcherName = matcher.name; + if (this.subsequence && (matcherName == "absent" || matcherName == "anyValue")) { + throw new Error(`The Matcher ${matcherName}() cannot be nested within arrayWith()`); + } + const innerResult = matcher.test(actual[actualIdx]); + if (!this.subsequence || !innerResult.hasFailed()) { + result.compose(`[${actualIdx}]`, innerResult); + patternIdx++; + actualIdx++; + } else { + actualIdx++; + } + } + for (; patternIdx < this.pattern.length; patternIdx++) { + const pattern = this.pattern[patternIdx]; + const element = Matcher.isMatcher(pattern) || typeof pattern === "object" ? " " : ` [${pattern}] `; + result.recordFailure({ + matcher: this, + path: [], + message: `Missing element${element}at pattern index ${patternIdx}` + }); + } + return result; + } +}; +var ObjectMatch = class extends Matcher { + constructor(name, pattern, options = {}) { + super(); + this.name = name; + this.pattern = pattern; + this.partial = options.partial ?? true; + } + test(actual) { + if (typeof actual !== "object" || Array.isArray(actual)) { + return new MatchResult(actual).recordFailure({ + matcher: this, + path: [], + message: `Expected type object but received ${getType(actual)}` + }); + } + const result = new MatchResult(actual); + if (!this.partial) { + for (const a of Object.keys(actual)) { + if (!(a in this.pattern)) { + result.recordFailure({ + matcher: this, + path: [`/${a}`], + message: "Unexpected key" + }); + } + } + } + for (const [patternKey, patternVal] of Object.entries(this.pattern)) { + if (!(patternKey in actual) && !(patternVal instanceof AbsentMatch)) { + result.recordFailure({ + matcher: this, + path: [`/${patternKey}`], + message: `Missing key '${patternKey}' among {${Object.keys(actual).join(",")}}` + }); + continue; + } + const matcher = Matcher.isMatcher(patternVal) ? patternVal : new LiteralMatch(this.name, patternVal, { partialObjects: this.partial }); + const inner = matcher.test(actual[patternKey]); + result.compose(`/${patternKey}`, inner); + } + return result; + } +}; +var SerializedJson = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const result = new MatchResult(actual); + if (getType(actual) !== "string") { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected JSON as a string but found ${getType(actual)}` + }); + return result; + } + let parsed; + try { + parsed = JSON.parse(actual); + } catch (err) { + if (err instanceof SyntaxError) { + result.recordFailure({ + matcher: this, + path: [], + message: `Invalid JSON string: ${actual}` + }); + return result; + } else { + throw err; + } + } + const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); + const innerResult = matcher.test(parsed); + result.compose(`(${this.name})`, innerResult); + return result; + } +}; +var NotMatch = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); + const innerResult = matcher.test(actual); + const result = new MatchResult(actual); + if (innerResult.failCount === 0) { + result.recordFailure({ + matcher: this, + path: [], + message: `Found unexpected match: ${JSON.stringify(actual, void 0, 2)}` + }); + } + return result; + } +}; +var AnyMatch = class extends Matcher { + constructor(name) { + super(); + this.name = name; + } + test(actual) { + const result = new MatchResult(actual); + if (actual == null) { + result.recordFailure({ + matcher: this, + path: [], + message: "Expected a value but found none" + }); + } + return result; + } +}; +var StringLikeRegexpMatch = class extends Matcher { + constructor(name, pattern) { + super(); + this.name = name; + this.pattern = pattern; + } + test(actual) { + const result = new MatchResult(actual); + const regex = new RegExp(this.pattern, "gm"); + if (typeof actual !== "string") { + result.recordFailure({ + matcher: this, + path: [], + message: `Expected a string, but got '${typeof actual}'` + }); + } + if (!regex.test(actual)) { + result.recordFailure({ + matcher: this, + path: [], + message: `String '${actual}' did not match pattern '${this.pattern}'` + }); + } + return result; + } +}; + +// lib/assertions/providers/lambda-handler/base.ts +var https = __toESM(require("https")); +var url = __toESM(require("url")); +var CustomResourceHandler = class { + constructor(event, context) { + this.event = event; + this.context = context; + this.timedOut = false; + this.timeout = setTimeout(async () => { + await this.respond({ + status: "FAILED", + reason: "Lambda Function Timeout", + data: this.context.logStreamName + }); + this.timedOut = true; + }, context.getRemainingTimeInMillis() - 1200); + this.event = event; + this.physicalResourceId = extractPhysicalResourceId(event); + } + async handle() { + try { + console.log(`Event: ${JSON.stringify(this.event)}`); + const response = await this.processEvent(this.event.ResourceProperties); + console.log(`Event output : ${JSON.stringify(response)}`); + await this.respond({ + status: "SUCCESS", + reason: "OK", + data: response + }); + } catch (e) { + console.log(e); + await this.respond({ + status: "FAILED", + reason: e.message ?? "Internal Error" + }); + } finally { + clearTimeout(this.timeout); + } + } + respond(response) { + if (this.timedOut) { + return; + } + const cfResponse = { + Status: response.status, + Reason: response.reason, + PhysicalResourceId: this.physicalResourceId, + StackId: this.event.StackId, + RequestId: this.event.RequestId, + LogicalResourceId: this.event.LogicalResourceId, + NoEcho: false, + Data: response.data + }; + const responseBody = JSON.stringify(cfResponse); + console.log("Responding to CloudFormation", responseBody); + const parsedUrl = url.parse(this.event.ResponseURL); + const requestOptions = { + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: "PUT", + headers: { "content-type": "", "content-length": responseBody.length } + }; + return new Promise((resolve, reject) => { + try { + const request2 = https.request(requestOptions, resolve); + request2.on("error", reject); + request2.write(responseBody); + request2.end(); + } catch (e) { + reject(e); + } + }); + } +}; +function extractPhysicalResourceId(event) { + switch (event.RequestType) { + case "Create": + return event.LogicalResourceId; + case "Update": + case "Delete": + return event.PhysicalResourceId; + } +} + +// lib/assertions/providers/lambda-handler/assertion.ts +var AssertionHandler = class extends CustomResourceHandler { + async processEvent(request2) { + let actual = decodeCall(request2.actual); + const expected = decodeCall(request2.expected); + let result; + const matcher = new MatchCreator(expected).getMatcher(); + console.log(`Testing equality between ${JSON.stringify(request2.actual)} and ${JSON.stringify(request2.expected)}`); + const matchResult = matcher.test(actual); + matchResult.finished(); + if (matchResult.hasFailed()) { + result = { + data: JSON.stringify({ + status: "fail", + message: [ + ...matchResult.toHumanStrings(), + JSON.stringify(matchResult.target, void 0, 2) + ].join("\n") + }) + }; + } else { + result = { + data: JSON.stringify({ + status: "pass" + }) + }; + } + return result; + } +}; +var MatchCreator = class { + constructor(obj) { + switch (Object.keys(obj)[0]) { + case "$ObjectLike": + this.type = "objectLike"; + this.parsedObj = obj.$ObjectLike; + break; + case "$ArrayWith": + this.type = "arrayWith"; + this.parsedObj = obj.$ArrayWith; + break; + case "$Exact": + this.type = "exact"; + this.parsedObj = obj.$Exact; + break; + case "$StringLike": + this.type = "stringLikeRegexp"; + this.parsedObj = obj.$StringLike; + break; + default: + this.type = "exact"; + this.parsedObj = obj; + } + } + getMatcher() { + try { + const final = JSON.parse(JSON.stringify(this.parsedObj), function(_k, v) { + const nested = Object.keys(v)[0]; + switch (nested) { + case "$ArrayWith": + return Match.arrayWith(v[nested]); + case "$ObjectLike": + return Match.objectLike(v[nested]); + case "$StringLike": + return Match.stringLikeRegexp(v[nested]); + default: + return v; + } + }); + return Match[this.type](final); + } catch { + return Match[this.type](this.parsedObj); + } + } +}; +function decodeCall(call) { + if (!call) { + return void 0; + } + try { + const parsed = JSON.parse(call); + return parsed; + } catch (e) { + return call; + } +} + +// lib/assertions/providers/lambda-handler/results.ts +var ResultsCollectionHandler = class extends CustomResourceHandler { + async processEvent(request2) { + const reduced = request2.assertionResults.reduce((agg, result, idx) => { + const msg = result.status === "pass" ? "pass" : `fail - ${result.message}`; + return `${agg} +Test${idx}: ${msg}`; + }, "").trim(); + return { message: reduced }; + } +}; + +// lib/assertions/providers/lambda-handler/utils.ts +function decode(object) { + return JSON.parse(JSON.stringify(object), (_k, v) => { + switch (v) { + case "TRUE:BOOLEAN": + return true; + case "FALSE:BOOLEAN": + return false; + default: + return v; + } + }); +} + +// lib/assertions/providers/lambda-handler/sdk.ts +function flatten(object) { + return Object.assign({}, ...function _flatten(child, path = []) { + return [].concat(...Object.keys(child).map((key) => { + const childKey = Buffer.isBuffer(child[key]) ? child[key].toString("utf8") : child[key]; + return typeof childKey === "object" && childKey !== null ? _flatten(childKey, path.concat([key])) : { [path.concat([key]).join(".")]: childKey }; + })); + }(object)); +} +var AwsApiCallHandler = class extends CustomResourceHandler { + async processEvent(request2) { + const AWS = require("aws-sdk"); + console.log(`AWS SDK VERSION: ${AWS.VERSION}`); + const service = new AWS[request2.service](); + const response = await service[request2.api](request2.parameters && decode(request2.parameters)).promise(); + console.log(`SDK response received ${JSON.stringify(response)}`); + delete response.ResponseMetadata; + const respond = { + apiCallResponse: response + }; + const flatData = __spreadValues({}, flatten(respond)); + return request2.flattenResponse === "true" ? flatData : respond; + } +}; + +// lib/assertions/providers/lambda-handler/types.ts +var ASSERT_RESOURCE_TYPE = "Custom::DeployAssert@AssertEquals"; +var RESULTS_RESOURCE_TYPE = "Custom::DeployAssert@ResultsCollection"; +var SDK_RESOURCE_TYPE_PREFIX = "Custom::DeployAssert@SdkCall"; + +// lib/assertions/providers/lambda-handler/index.ts +async function handler(event, context) { + const provider = createResourceHandler(event, context); + await provider.handle(); +} +function createResourceHandler(event, context) { + if (event.ResourceType.startsWith(SDK_RESOURCE_TYPE_PREFIX)) { + return new AwsApiCallHandler(event, context); + } + switch (event.ResourceType) { + case ASSERT_RESOURCE_TYPE: + return new AssertionHandler(event, context); + case RESULTS_RESOURCE_TYPE: + return new ResultsCollectionHandler(event, context); + default: + throw new Error(`Unsupported resource type "${event.ResourceType}`); + } +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + handler +}); diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle/index.js b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle/index.js deleted file mode 100644 index 3a860016dea59..0000000000000 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle/index.js +++ /dev/null @@ -1,643 +0,0 @@ -var __create = Object.create; -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __getOwnPropSymbols = Object.getOwnPropertySymbols; -var __getProtoOf = Object.getPrototypeOf; -var __hasOwnProp = Object.prototype.hasOwnProperty; -var __propIsEnum = Object.prototype.propertyIsEnumerable; -var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; -var __spreadValues = (a, b) => { - for (var prop in b || (b = {})) - if (__hasOwnProp.call(b, prop)) - __defNormalProp(a, prop, b[prop]); - if (__getOwnPropSymbols) - for (var prop of __getOwnPropSymbols(b)) { - if (__propIsEnum.call(b, prop)) - __defNormalProp(a, prop, b[prop]); - } - return a; -}; -var __esm = (fn, res) => function __init() { - return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; -}; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { get: all[name], enumerable: true }); -}; -var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; -}; -var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); -var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); - -// ../assertions/lib/matcher.ts -var Matcher, MatchResult; -var init_matcher = __esm({ - "../assertions/lib/matcher.ts"() { - Matcher = class { - static isMatcher(x) { - return x && x instanceof Matcher; - } - }; - MatchResult = class { - constructor(target) { - this.failures = []; - this.captures = /* @__PURE__ */ new Map(); - this.finalized = false; - this.target = target; - } - push(matcher, path, message) { - return this.recordFailure({ matcher, path, message }); - } - recordFailure(failure) { - this.failures.push(failure); - return this; - } - hasFailed() { - return this.failures.length !== 0; - } - get failCount() { - return this.failures.length; - } - compose(id, inner) { - const innerF = inner.failures; - this.failures.push(...innerF.map((f) => { - return { path: [id, ...f.path], message: f.message, matcher: f.matcher }; - })); - inner.captures.forEach((vals, capture) => { - vals.forEach((value) => this.recordCapture({ capture, value })); - }); - return this; - } - finished() { - if (this.finalized) { - return this; - } - if (this.failCount === 0) { - this.captures.forEach((vals, cap) => cap._captured.push(...vals)); - } - this.finalized = true; - return this; - } - toHumanStrings() { - return this.failures.map((r) => { - const loc = r.path.length === 0 ? "" : ` at ${r.path.join("")}`; - return "" + r.message + loc + ` (using ${r.matcher.name} matcher)`; - }); - } - recordCapture(options) { - let values = this.captures.get(options.capture); - if (values === void 0) { - values = []; - } - values.push(options.value); - this.captures.set(options.capture, values); - } - }; - } -}); - -// ../assertions/lib/private/matchers/absent.ts -var AbsentMatch; -var init_absent = __esm({ - "../assertions/lib/private/matchers/absent.ts"() { - init_matcher(); - AbsentMatch = class extends Matcher { - constructor(name) { - super(); - this.name = name; - } - test(actual) { - const result = new MatchResult(actual); - if (actual !== void 0) { - result.recordFailure({ - matcher: this, - path: [], - message: `Received ${actual}, but key should be absent` - }); - } - return result; - } - }; - } -}); - -// ../assertions/lib/private/type.ts -function getType(obj) { - return Array.isArray(obj) ? "array" : typeof obj; -} -var init_type = __esm({ - "../assertions/lib/private/type.ts"() { - } -}); - -// ../assertions/lib/match.ts -var match_exports = {}; -__export(match_exports, { - Match: () => Match -}); -var Match, LiteralMatch, ArrayMatch, ObjectMatch, SerializedJson, NotMatch, AnyMatch, StringLikeRegexpMatch; -var init_match = __esm({ - "../assertions/lib/match.ts"() { - init_matcher(); - init_absent(); - init_type(); - Match = class { - static absent() { - return new AbsentMatch("absent"); - } - static arrayWith(pattern) { - return new ArrayMatch("arrayWith", pattern); - } - static arrayEquals(pattern) { - return new ArrayMatch("arrayEquals", pattern, { subsequence: false }); - } - static exact(pattern) { - return new LiteralMatch("exact", pattern, { partialObjects: false }); - } - static objectLike(pattern) { - return new ObjectMatch("objectLike", pattern); - } - static objectEquals(pattern) { - return new ObjectMatch("objectEquals", pattern, { partial: false }); - } - static not(pattern) { - return new NotMatch("not", pattern); - } - static serializedJson(pattern) { - return new SerializedJson("serializedJson", pattern); - } - static anyValue() { - return new AnyMatch("anyValue"); - } - static stringLikeRegexp(pattern) { - return new StringLikeRegexpMatch("stringLikeRegexp", pattern); - } - }; - LiteralMatch = class extends Matcher { - constructor(name, pattern, options = {}) { - super(); - this.name = name; - this.pattern = pattern; - this.partialObjects = options.partialObjects ?? false; - if (Matcher.isMatcher(this.pattern)) { - throw new Error("LiteralMatch cannot directly contain another matcher. Remove the top-level matcher or nest it more deeply."); - } - } - test(actual) { - if (Array.isArray(this.pattern)) { - return new ArrayMatch(this.name, this.pattern, { subsequence: false, partialObjects: this.partialObjects }).test(actual); - } - if (typeof this.pattern === "object") { - return new ObjectMatch(this.name, this.pattern, { partial: this.partialObjects }).test(actual); - } - const result = new MatchResult(actual); - if (typeof this.pattern !== typeof actual) { - result.recordFailure({ - matcher: this, - path: [], - message: `Expected type ${typeof this.pattern} but received ${getType(actual)}` - }); - return result; - } - if (actual !== this.pattern) { - result.recordFailure({ - matcher: this, - path: [], - message: `Expected ${this.pattern} but received ${actual}` - }); - } - return result; - } - }; - ArrayMatch = class extends Matcher { - constructor(name, pattern, options = {}) { - super(); - this.name = name; - this.pattern = pattern; - this.subsequence = options.subsequence ?? true; - this.partialObjects = options.partialObjects ?? false; - } - test(actual) { - if (!Array.isArray(actual)) { - return new MatchResult(actual).recordFailure({ - matcher: this, - path: [], - message: `Expected type array but received ${getType(actual)}` - }); - } - if (!this.subsequence && this.pattern.length !== actual.length) { - return new MatchResult(actual).recordFailure({ - matcher: this, - path: [], - message: `Expected array of length ${this.pattern.length} but received ${actual.length}` - }); - } - let patternIdx = 0; - let actualIdx = 0; - const result = new MatchResult(actual); - while (patternIdx < this.pattern.length && actualIdx < actual.length) { - const patternElement = this.pattern[patternIdx]; - const matcher = Matcher.isMatcher(patternElement) ? patternElement : new LiteralMatch(this.name, patternElement, { partialObjects: this.partialObjects }); - const matcherName = matcher.name; - if (this.subsequence && (matcherName == "absent" || matcherName == "anyValue")) { - throw new Error(`The Matcher ${matcherName}() cannot be nested within arrayWith()`); - } - const innerResult = matcher.test(actual[actualIdx]); - if (!this.subsequence || !innerResult.hasFailed()) { - result.compose(`[${actualIdx}]`, innerResult); - patternIdx++; - actualIdx++; - } else { - actualIdx++; - } - } - for (; patternIdx < this.pattern.length; patternIdx++) { - const pattern = this.pattern[patternIdx]; - const element = Matcher.isMatcher(pattern) || typeof pattern === "object" ? " " : ` [${pattern}] `; - result.recordFailure({ - matcher: this, - path: [], - message: `Missing element${element}at pattern index ${patternIdx}` - }); - } - return result; - } - }; - ObjectMatch = class extends Matcher { - constructor(name, pattern, options = {}) { - super(); - this.name = name; - this.pattern = pattern; - this.partial = options.partial ?? true; - } - test(actual) { - if (typeof actual !== "object" || Array.isArray(actual)) { - return new MatchResult(actual).recordFailure({ - matcher: this, - path: [], - message: `Expected type object but received ${getType(actual)}` - }); - } - const result = new MatchResult(actual); - if (!this.partial) { - for (const a of Object.keys(actual)) { - if (!(a in this.pattern)) { - result.recordFailure({ - matcher: this, - path: [`/${a}`], - message: "Unexpected key" - }); - } - } - } - for (const [patternKey, patternVal] of Object.entries(this.pattern)) { - if (!(patternKey in actual) && !(patternVal instanceof AbsentMatch)) { - result.recordFailure({ - matcher: this, - path: [`/${patternKey}`], - message: `Missing key '${patternKey}' among {${Object.keys(actual).join(",")}}` - }); - continue; - } - const matcher = Matcher.isMatcher(patternVal) ? patternVal : new LiteralMatch(this.name, patternVal, { partialObjects: this.partial }); - const inner = matcher.test(actual[patternKey]); - result.compose(`/${patternKey}`, inner); - } - return result; - } - }; - SerializedJson = class extends Matcher { - constructor(name, pattern) { - super(); - this.name = name; - this.pattern = pattern; - } - test(actual) { - const result = new MatchResult(actual); - if (getType(actual) !== "string") { - result.recordFailure({ - matcher: this, - path: [], - message: `Expected JSON as a string but found ${getType(actual)}` - }); - return result; - } - let parsed; - try { - parsed = JSON.parse(actual); - } catch (err) { - if (err instanceof SyntaxError) { - result.recordFailure({ - matcher: this, - path: [], - message: `Invalid JSON string: ${actual}` - }); - return result; - } else { - throw err; - } - } - const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); - const innerResult = matcher.test(parsed); - result.compose(`(${this.name})`, innerResult); - return result; - } - }; - NotMatch = class extends Matcher { - constructor(name, pattern) { - super(); - this.name = name; - this.pattern = pattern; - } - test(actual) { - const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern); - const innerResult = matcher.test(actual); - const result = new MatchResult(actual); - if (innerResult.failCount === 0) { - result.recordFailure({ - matcher: this, - path: [], - message: `Found unexpected match: ${JSON.stringify(actual, void 0, 2)}` - }); - } - return result; - } - }; - AnyMatch = class extends Matcher { - constructor(name) { - super(); - this.name = name; - } - test(actual) { - const result = new MatchResult(actual); - if (actual == null) { - result.recordFailure({ - matcher: this, - path: [], - message: "Expected a value but found none" - }); - } - return result; - } - }; - StringLikeRegexpMatch = class extends Matcher { - constructor(name, pattern) { - super(); - this.name = name; - this.pattern = pattern; - } - test(actual) { - const result = new MatchResult(actual); - const regex = new RegExp(this.pattern, "gm"); - if (typeof actual !== "string") { - result.recordFailure({ - matcher: this, - path: [], - message: `Expected a string, but got '${typeof actual}'` - }); - } - if (!regex.test(actual)) { - result.recordFailure({ - matcher: this, - path: [], - message: `String '${actual}' did not match pattern '${this.pattern}'` - }); - } - return result; - } - }; - } -}); - -// lib/assertions/providers/lambda-handler/index.ts -var lambda_handler_exports = {}; -__export(lambda_handler_exports, { - handler: () => handler -}); -module.exports = __toCommonJS(lambda_handler_exports); - -// lib/assertions/providers/lambda-handler/base.ts -var https = __toESM(require("https")); -var url = __toESM(require("url")); -var CustomResourceHandler = class { - constructor(event, context) { - this.event = event; - this.context = context; - this.timedOut = false; - this.timeout = setTimeout(async () => { - await this.respond({ - status: "FAILED", - reason: "Lambda Function Timeout", - data: this.context.logStreamName - }); - this.timedOut = true; - }, context.getRemainingTimeInMillis() - 1200); - this.event = event; - this.physicalResourceId = extractPhysicalResourceId(event); - } - async handle() { - try { - console.log(`Event: ${JSON.stringify(this.event)}`); - const response = await this.processEvent(this.event.ResourceProperties); - console.log(`Event output : ${JSON.stringify(response)}`); - await this.respond({ - status: "SUCCESS", - reason: "OK", - data: response - }); - } catch (e) { - console.log(e); - await this.respond({ - status: "FAILED", - reason: e.message ?? "Internal Error" - }); - } finally { - clearTimeout(this.timeout); - } - } - respond(response) { - if (this.timedOut) { - return; - } - const cfResponse = { - Status: response.status, - Reason: response.reason, - PhysicalResourceId: this.physicalResourceId, - StackId: this.event.StackId, - RequestId: this.event.RequestId, - LogicalResourceId: this.event.LogicalResourceId, - NoEcho: false, - Data: response.data - }; - const responseBody = JSON.stringify(cfResponse); - console.log("Responding to CloudFormation", responseBody); - const parsedUrl = url.parse(this.event.ResponseURL); - const requestOptions = { - hostname: parsedUrl.hostname, - path: parsedUrl.path, - method: "PUT", - headers: { "content-type": "", "content-length": responseBody.length } - }; - return new Promise((resolve, reject) => { - try { - const request2 = https.request(requestOptions, resolve); - request2.on("error", reject); - request2.write(responseBody); - request2.end(); - } catch (e) { - reject(e); - } - }); - } -}; -function extractPhysicalResourceId(event) { - switch (event.RequestType) { - case "Create": - return event.LogicalResourceId; - case "Update": - case "Delete": - return event.PhysicalResourceId; - } -} - -// lib/assertions/providers/lambda-handler/assertion.ts -var { Match: Match2 } = (init_match(), __toCommonJS(match_exports)); -var AssertionHandler = class extends CustomResourceHandler { - async processEvent(request2) { - let actual = request2.actual; - const expected = decodeCall(request2.expected); - let result; - let matcher; - console.log(`Testing equality between ${JSON.stringify(request2.actual)} and ${JSON.stringify(request2.expected)}`); - switch (request2.assertionType) { - case "objectLike": - if (typeof actual === "string") { - actual = JSON.parse(actual); - } - matcher = Match2.objectLike(expected); - break; - case "equals": - matcher = Match2.exact(expected); - break; - case "arrayWith": - matcher = Match2.arrayWith(expected); - break; - default: - throw new Error(`Unsupported query type ${request2.assertionType}`); - } - const matchResult = matcher.test(actual); - matchResult.finished(); - if (matchResult.hasFailed()) { - result = { - data: JSON.stringify({ - status: "fail", - message: [ - ...matchResult.toHumanStrings(), - JSON.stringify(matchResult.target, void 0, 2) - ].join("\n") - }) - }; - } else { - result = { - data: JSON.stringify({ - status: "pass" - }) - }; - } - return result; - } -}; -function decodeCall(call) { - if (!call) { - return void 0; - } - return JSON.parse(call); -} - -// lib/assertions/providers/lambda-handler/results.ts -var ResultsCollectionHandler = class extends CustomResourceHandler { - async processEvent(request2) { - const reduced = request2.assertionResults.reduce((agg, result, idx) => { - const msg = result.status === "pass" ? "pass" : `fail - ${result.message}`; - return `${agg} -Test${idx}: ${msg}`; - }, "").trim(); - return { message: reduced }; - } -}; - -// lib/assertions/providers/lambda-handler/utils.ts -function decode(object) { - return JSON.parse(JSON.stringify(object), (_k, v) => { - switch (v) { - case "TRUE:BOOLEAN": - return true; - case "FALSE:BOOLEAN": - return false; - case !isNaN(+v): - return +v; - default: - return v; - } - }); -} - -// lib/assertions/providers/lambda-handler/sdk.ts -function flatten(object) { - return Object.assign({}, ...function _flatten(child, path = []) { - return [].concat(...Object.keys(child).map((key) => { - const childKey = Buffer.isBuffer(child[key]) ? child[key].toString("utf8") : child[key]; - return typeof childKey === "object" && childKey !== null ? _flatten(childKey, path.concat([key])) : { [path.concat([key]).join(".")]: childKey }; - })); - }(object)); -} -var SdkHandler = class extends CustomResourceHandler { - async processEvent(request2) { - const AWS = require("aws-sdk"); - console.log(`AWS SDK VERSION: ${AWS.VERSION}`); - const service = new AWS[request2.service](); - const response = await service[request2.api](request2.parameters && decode(request2.parameters)).promise(); - console.log(`SDK response received ${JSON.stringify(response)}`); - delete response.ResponseMetadata; - const respond = { - apiCallResponse: response - }; - const flatData = __spreadValues({}, flatten(respond)); - return request2.flattenResponse === "true" ? flatData : respond; - } -}; - -// lib/assertions/providers/lambda-handler/types.ts -var ASSERT_RESOURCE_TYPE = "Custom::DeployAssert@AssertEquals"; -var RESULTS_RESOURCE_TYPE = "Custom::DeployAssert@ResultsCollection"; -var SDK_RESOURCE_TYPE_PREFIX = "Custom::DeployAssert@SdkCall"; - -// lib/assertions/providers/lambda-handler/index.ts -async function handler(event, context) { - const provider = createResourceHandler(event, context); - await provider.handle(); -} -function createResourceHandler(event, context) { - if (event.ResourceType.startsWith(SDK_RESOURCE_TYPE_PREFIX)) { - return new SdkHandler(event, context); - } - switch (event.ResourceType) { - case ASSERT_RESOURCE_TYPE: - return new AssertionHandler(event, context); - case RESULTS_RESOURCE_TYPE: - return new ResultsCollectionHandler(event, context); - default: - throw new Error(`Unsupported resource type "${event.ResourceType}`); - } -} -// Annotate the CommonJS export names for ESM import in node: -0 && (module.exports = { - handler -}); diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/cdk.out index 2efc89439fab8..ccdfc1ff96a9d 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/cdk.out +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/cdk.out @@ -1 +1 @@ -{"version":"18.0.0"} \ No newline at end of file +{"version":"19.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/integ.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/integ.json index 8f6868201ff0d..078110c0feb39 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/integ.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/integ.json @@ -1,11 +1,11 @@ { - "version": "18.0.0", + "version": "19.0.0", "testCases": { - "DefaultTestCase": { + "PutEvents/DefaultTest": { "stacks": [ "aws-stepfunctions-tasks-eventbridge-put-events-integ" ], - "assertionStack": "IntegTestDefaultTestCaseDeployAssert4F885E41" + "assertionStack": "PutEventsDefaultTestDeployAssert1A6BA3F3" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/manifest.json index 86b2d7285740d..ed816bb0e425f 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "18.0.0", + "version": "19.0.0", "artifacts": { "Tree": { "type": "cdk:tree", @@ -54,87 +54,87 @@ }, "displayName": "aws-stepfunctions-tasks-eventbridge-put-events-integ" }, - "IntegTestDefaultTestCaseDeployAssert4F885E41": { + "PutEventsDefaultTestDeployAssert1A6BA3F3": { "type": "aws:cloudformation:stack", "environment": "aws://unknown-account/unknown-region", "properties": { - "templateFile": "IntegTestDefaultTestCaseDeployAssert4F885E41.template.json", + "templateFile": "PutEventsDefaultTestDeployAssert1A6BA3F3.template.json", "validateOnSynth": false }, "dependencies": [ "aws-stepfunctions-tasks-eventbridge-put-events-integ" ], "metadata": { - "/IntegTest/DefaultTestCase/DeployAssert": [ + "/PutEvents/DefaultTest/DeployAssert": [ { "type": "aws:cdk:asset", "data": { - "path": "asset.37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0.bundle", - "id": "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", + "path": "asset.1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b.bundle", + "id": "1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b", "packaging": "zip", - "sourceHash": "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", - "s3BucketParameter": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB", - "s3KeyParameter": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4", - "artifactHashParameter": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0ArtifactHash4C05AFBC" + "sourceHash": "1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b", + "s3BucketParameter": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3BucketF7210344", + "s3KeyParameter": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3VersionKey1E71961C", + "artifactHashParameter": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bArtifactHash4F8362F2" } } ], - "/IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsstartExecution/Default/Default": [ + "/PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsstartExecution/Default/Default": [ { "type": "aws:cdk:logicalId", - "data": "SdkQueryStepFunctionsstartExecution" + "data": "AwsApiCallStepFunctionsstartExecution" } ], - "/IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/Default/Default": [ + "/PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsdescribeExecution/Default/Default": [ { "type": "aws:cdk:logicalId", - "data": "SdkQueryStepFunctionsdescribeExecution" + "data": "AwsApiCallStepFunctionsdescribeExecution" } ], - "/IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/EqualsAssertionStepFunctionsdescribeExecution/Default/Default": [ + "/PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsdescribeExecution/AssertEqualsStepFunctionsdescribeExecution/Default/Default": [ { "type": "aws:cdk:logicalId", - "data": "SdkQueryStepFunctionsdescribeExecutionEqualsAssertionStepFunctionsdescribeExecutionA4C8D5D7" + "data": "AwsApiCallStepFunctionsdescribeExecutionAssertEqualsStepFunctionsdescribeExecution58E75A69" } ], - "/IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/EqualsAssertionStepFunctionsdescribeExecution/AssertionResults": [ + "/PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsdescribeExecution/AssertEqualsStepFunctionsdescribeExecution/AssertionResults": [ { "type": "aws:cdk:logicalId", - "data": "AssertionResultsEqualsAssertionStepFunctionsdescribeExecution" + "data": "AssertionResultsAssertEqualsStepFunctionsdescribeExecution" } ], - "/IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role": [ + "/PutEvents/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role": [ { "type": "aws:cdk:logicalId", "data": "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73" } ], - "/IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler": [ + "/PutEvents/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler": [ { "type": "aws:cdk:logicalId", "data": "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F" } ], - "/IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3Bucket": [ + "/PutEvents/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b/S3Bucket": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3BucketD8E6ACBB" + "data": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3BucketF7210344" } ], - "/IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3VersionKey": [ + "/PutEvents/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b/S3VersionKey": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0S3VersionKey4EA626F4" + "data": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bS3VersionKey1E71961C" } ], - "/IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/ArtifactHash": [ + "/PutEvents/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b/ArtifactHash": [ { "type": "aws:cdk:logicalId", - "data": "AssetParameters37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0ArtifactHash4C05AFBC" + "data": "AssetParameters1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38bArtifactHash4F8362F2" } ] }, - "displayName": "IntegTest/DefaultTestCase/DeployAssert" + "displayName": "PutEvents/DefaultTest/DeployAssert" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/tree.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/tree.json index 5e93ec6bff393..da0158eadc152 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/tree.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/eventbridge/put-events.integ.snapshot/tree.json @@ -237,33 +237,33 @@ "version": "0.0.0" } }, - "IntegTest": { - "id": "IntegTest", - "path": "IntegTest", + "PutEvents": { + "id": "PutEvents", + "path": "PutEvents", "children": { - "DefaultTestCase": { - "id": "DefaultTestCase", - "path": "IntegTest/DefaultTestCase", + "DefaultTest": { + "id": "DefaultTest", + "path": "PutEvents/DefaultTest", "children": { "DeployAssert": { "id": "DeployAssert", - "path": "IntegTest/DefaultTestCase/DeployAssert", + "path": "PutEvents/DefaultTest/DeployAssert", "children": { "Default": { "id": "Default", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default", + "path": "PutEvents/DefaultTest/DeployAssert/Default", "children": { - "SdkQueryStepFunctionsstartExecution": { - "id": "SdkQueryStepFunctionsstartExecution", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsstartExecution", + "AwsApiCallStepFunctionsstartExecution": { + "id": "AwsApiCallStepFunctionsstartExecution", + "path": "PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsstartExecution", "children": { "SdkProvider": { "id": "SdkProvider", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsstartExecution/SdkProvider", + "path": "PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsstartExecution/SdkProvider", "children": { "AssertionsProvider": { "id": "AssertionsProvider", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsstartExecution/SdkProvider/AssertionsProvider", + "path": "PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsstartExecution/SdkProvider/AssertionsProvider", "constructInfo": { "fqn": "@aws-cdk/core.Construct", "version": "0.0.0" @@ -277,11 +277,11 @@ }, "Default": { "id": "Default", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsstartExecution/Default", + "path": "PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsstartExecution/Default", "children": { "Default": { "id": "Default", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsstartExecution/Default/Default", + "path": "PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsstartExecution/Default/Default", "constructInfo": { "fqn": "@aws-cdk/core.CfnResource", "version": "0.0.0" @@ -295,21 +295,21 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/integ-tests.SdkQuery", + "fqn": "@aws-cdk/integ-tests.AwsApiCall", "version": "0.0.0" } }, - "SdkQueryStepFunctionsdescribeExecution": { - "id": "SdkQueryStepFunctionsdescribeExecution", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution", + "AwsApiCallStepFunctionsdescribeExecution": { + "id": "AwsApiCallStepFunctionsdescribeExecution", + "path": "PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsdescribeExecution", "children": { "SdkProvider": { "id": "SdkProvider", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/SdkProvider", + "path": "PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsdescribeExecution/SdkProvider", "children": { "AssertionsProvider": { "id": "AssertionsProvider", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/SdkProvider/AssertionsProvider", + "path": "PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsdescribeExecution/SdkProvider/AssertionsProvider", "constructInfo": { "fqn": "@aws-cdk/core.Construct", "version": "0.0.0" @@ -323,11 +323,11 @@ }, "Default": { "id": "Default", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/Default", + "path": "PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsdescribeExecution/Default", "children": { "Default": { "id": "Default", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/Default/Default", + "path": "PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsdescribeExecution/Default/Default", "constructInfo": { "fqn": "@aws-cdk/core.CfnResource", "version": "0.0.0" @@ -339,17 +339,17 @@ "version": "0.0.0" } }, - "EqualsAssertionStepFunctionsdescribeExecution": { - "id": "EqualsAssertionStepFunctionsdescribeExecution", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/EqualsAssertionStepFunctionsdescribeExecution", + "AssertEqualsStepFunctionsdescribeExecution": { + "id": "AssertEqualsStepFunctionsdescribeExecution", + "path": "PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsdescribeExecution/AssertEqualsStepFunctionsdescribeExecution", "children": { "AssertionProvider": { "id": "AssertionProvider", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/EqualsAssertionStepFunctionsdescribeExecution/AssertionProvider", + "path": "PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsdescribeExecution/AssertEqualsStepFunctionsdescribeExecution/AssertionProvider", "children": { "AssertionsProvider": { "id": "AssertionsProvider", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/EqualsAssertionStepFunctionsdescribeExecution/AssertionProvider/AssertionsProvider", + "path": "PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsdescribeExecution/AssertEqualsStepFunctionsdescribeExecution/AssertionProvider/AssertionsProvider", "constructInfo": { "fqn": "@aws-cdk/core.Construct", "version": "0.0.0" @@ -363,11 +363,11 @@ }, "Default": { "id": "Default", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/EqualsAssertionStepFunctionsdescribeExecution/Default", + "path": "PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsdescribeExecution/AssertEqualsStepFunctionsdescribeExecution/Default", "children": { "Default": { "id": "Default", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/EqualsAssertionStepFunctionsdescribeExecution/Default/Default", + "path": "PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsdescribeExecution/AssertEqualsStepFunctionsdescribeExecution/Default/Default", "constructInfo": { "fqn": "@aws-cdk/core.CfnResource", "version": "0.0.0" @@ -381,7 +381,7 @@ }, "AssertionResults": { "id": "AssertionResults", - "path": "IntegTest/DefaultTestCase/DeployAssert/Default/SdkQueryStepFunctionsdescribeExecution/EqualsAssertionStepFunctionsdescribeExecution/AssertionResults", + "path": "PutEvents/DefaultTest/DeployAssert/Default/AwsApiCallStepFunctionsdescribeExecution/AssertEqualsStepFunctionsdescribeExecution/AssertionResults", "constructInfo": { "fqn": "@aws-cdk/core.CfnOutput", "version": "0.0.0" @@ -395,7 +395,7 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/integ-tests.SdkQuery", + "fqn": "@aws-cdk/integ-tests.AwsApiCall", "version": "0.0.0" } } @@ -407,11 +407,11 @@ }, "SingletonFunction1488541a7b23466481b69b4408076b81": { "id": "SingletonFunction1488541a7b23466481b69b4408076b81", - "path": "IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81", + "path": "PutEvents/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81", "children": { "Staging": { "id": "Staging", - "path": "IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Staging", + "path": "PutEvents/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Staging", "constructInfo": { "fqn": "@aws-cdk/core.AssetStaging", "version": "0.0.0" @@ -419,7 +419,7 @@ }, "Role": { "id": "Role", - "path": "IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role", + "path": "PutEvents/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role", "constructInfo": { "fqn": "@aws-cdk/core.CfnResource", "version": "0.0.0" @@ -427,7 +427,7 @@ }, "Handler": { "id": "Handler", - "path": "IntegTest/DefaultTestCase/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler", + "path": "PutEvents/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler", "constructInfo": { "fqn": "@aws-cdk/core.CfnResource", "version": "0.0.0" @@ -441,15 +441,15 @@ }, "AssetParameters": { "id": "AssetParameters", - "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters", + "path": "PutEvents/DefaultTest/DeployAssert/AssetParameters", "children": { - "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0": { - "id": "37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", - "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0", + "1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b": { + "id": "1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b", + "path": "PutEvents/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b", "children": { "S3Bucket": { "id": "S3Bucket", - "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3Bucket", + "path": "PutEvents/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b/S3Bucket", "constructInfo": { "fqn": "@aws-cdk/core.CfnParameter", "version": "0.0.0" @@ -457,7 +457,7 @@ }, "S3VersionKey": { "id": "S3VersionKey", - "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/S3VersionKey", + "path": "PutEvents/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b/S3VersionKey", "constructInfo": { "fqn": "@aws-cdk/core.CfnParameter", "version": "0.0.0" @@ -465,7 +465,7 @@ }, "ArtifactHash": { "id": "ArtifactHash", - "path": "IntegTest/DefaultTestCase/DeployAssert/AssetParameters/37ffa65dc0e0569fbdcfa87537a30e1bb540e93a747030a1074829694dedd7b0/ArtifactHash", + "path": "PutEvents/DefaultTest/DeployAssert/AssetParameters/1bc7cf3a01a7153f942391263b3bac937812996cc28f9abaf83ffebbbe03e38b/ArtifactHash", "constructInfo": { "fqn": "@aws-cdk/core.CfnParameter", "version": "0.0.0" @@ -491,13 +491,13 @@ } }, "constructInfo": { - "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "fqn": "@aws-cdk/core.Construct", "version": "0.0.0" } } }, "constructInfo": { - "fqn": "@aws-cdk/integ-tests.IntegTest", + "fqn": "@aws-cdk/core.Construct", "version": "0.0.0" } } diff --git a/packages/@aws-cdk/integ-runner/lib/workers/extract/extract_worker.ts b/packages/@aws-cdk/integ-runner/lib/workers/extract/extract_worker.ts index 948329eddee1c..c38d97a55856c 100644 --- a/packages/@aws-cdk/integ-runner/lib/workers/extract/extract_worker.ts +++ b/packages/@aws-cdk/integ-runner/lib/workers/extract/extract_worker.ts @@ -42,14 +42,14 @@ export function integTestWorker(request: IntegTestBatchRequest): IntegTestWorker failures.push(test); workerpool.workerEmit({ reason: DiagnosticReason.ASSERTION_FAILED, - testName: testCaseName, + testName: `${runner.testName}-${testCaseName} (${request.profile}/${request.region})`, message: formatAssertionResults(results), duration: (Date.now() - start) / 1000, }); } else { workerpool.workerEmit({ reason: DiagnosticReason.TEST_SUCCESS, - testName: testCaseName, + testName: `${runner.testName}-${testCaseName}`, message: 'Success', duration: (Date.now() - start) / 1000, }); @@ -58,7 +58,7 @@ export function integTestWorker(request: IntegTestBatchRequest): IntegTestWorker failures.push(test); workerpool.workerEmit({ reason: DiagnosticReason.TEST_FAILED, - testName: testCaseName, + testName: `${runner.testName}-${testCaseName} (${request.profile}/${request.region})`, message: `Integration test failed: ${e}`, duration: (Date.now() - start) / 1000, }); @@ -68,7 +68,7 @@ export function integTestWorker(request: IntegTestBatchRequest): IntegTestWorker failures.push(test); workerpool.workerEmit({ reason: DiagnosticReason.TEST_FAILED, - testName: test.fileName, + testName: `${test.fileName} (${request.profile}/${request.region})`, message: `Integration test failed: ${e}`, duration: (Date.now() - start) / 1000, }); diff --git a/packages/@aws-cdk/integ-runner/test/test-data/test-with-snapshot-assets-diff.integ.snapshot/manifest.json b/packages/@aws-cdk/integ-runner/test/test-data/test-with-snapshot-assets-diff.integ.snapshot/manifest.json index 0ee57e25c6164..d1c7a0ea225c1 100644 --- a/packages/@aws-cdk/integ-runner/test/test-data/test-with-snapshot-assets-diff.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/integ-runner/test/test-data/test-with-snapshot-assets-diff.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "18.0.0", + "version": "19.0.0", "artifacts": { "Tree": { "type": "cdk:tree", diff --git a/packages/@aws-cdk/integ-runner/test/test-data/test-with-snapshot-assets.integ.snapshot/manifest.json b/packages/@aws-cdk/integ-runner/test/test-data/test-with-snapshot-assets.integ.snapshot/manifest.json index b0fce9b7b6d09..8ef72f9af40ea 100644 --- a/packages/@aws-cdk/integ-runner/test/test-data/test-with-snapshot-assets.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/integ-runner/test/test-data/test-with-snapshot-assets.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "18.0.0", + "version": "19.0.0", "artifacts": { "Tree": { "type": "cdk:tree", diff --git a/packages/@aws-cdk/integ-runner/test/test-data/test-with-snapshot.integ.snapshot/manifest.json b/packages/@aws-cdk/integ-runner/test/test-data/test-with-snapshot.integ.snapshot/manifest.json index 4be1dff08ac1e..bc0e09d1ce230 100644 --- a/packages/@aws-cdk/integ-runner/test/test-data/test-with-snapshot.integ.snapshot/manifest.json +++ b/packages/@aws-cdk/integ-runner/test/test-data/test-with-snapshot.integ.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "18.0.0", + "version": "19.0.0", "artifacts": { "Tree": { "type": "cdk:tree", diff --git a/packages/@aws-cdk/integ-tests/README.md b/packages/@aws-cdk/integ-tests/README.md index d3b982769f5d0..0e8fc9b1ca501 100644 --- a/packages/@aws-cdk/integ-tests/README.md +++ b/packages/@aws-cdk/integ-tests/README.md @@ -211,9 +211,9 @@ new AwsApiCall(assert, 'GetObject', { declare const myCustomResource: CustomResource; declare const app: App; const assert = new DeployAssert(app); -assert.strictEquals( +assert.assert( 'CustomAssertion', - ExpectedResult.fromObject({ foo: 'bar' }), + ExpectedResult.objectLike({ foo: 'bar' }), ActualResult.fromCustomResource(myCustomResource, 'data'), ); ``` @@ -283,7 +283,7 @@ const message = integ.assert.awsApiCall('SQS', 'receiveMessage', { new EqualsAssertion(integ.assert, 'ReceiveMessage', { actual: ActualResult.fromAwsApiCall(message, 'Messages.0.Body'), - expected: ExpectedResult.fromObject({ + expected: ExpectedResult.objectLike({ requestContext: { condition: 'Success', }, @@ -295,10 +295,30 @@ new EqualsAssertion(integ.assert, 'ReceiveMessage', { }, responsePayload: 'success', }), - assertionType: AssertionType.OBJECT_LIKE, }); ``` +#### Match + +`integ-tests` also provides a `Match` utility similar to the `@aws-cdk/assertions` module. `Match` +can be used to construct the `ExpectedResult`. + +```ts +declare const message: AwsApiCall; +declare const assert: DeployAssert; + +message.assert(ExpectedResult.objectLike({ + Messages: Match.arrayWith([ + { + Body: { + Values: Match.arrayWith([{ Asdf: 3 }]), + Message: Match.stringLikeRegexp('message'), + }, + }, + ]), +})); +``` + ### Examples #### Invoke a Lambda Function @@ -319,9 +339,9 @@ const integ = new IntegTest(app, 'IntegTest', { const invoke = integ.assert.invokeFunction({ functionName: lambdaFunction.functionName, }); -invoke.assertObjectLike({ +invoke.assert(ExpectedResult.objectLike({ Payload: '200', -}); +})); ``` #### Make an AWS API Call @@ -349,8 +369,8 @@ const describe = testCase.assert.awsApiCall('StepFunctions', 'describeExecution' }); // assert the results -describe.assertObjectLike({ +describe.assert(ExpectedResult.objectLike({ status: 'SUCCEEDED', -}); +})); ``` diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/common.ts b/packages/@aws-cdk/integ-tests/lib/assertions/common.ts index 316d36848492d..6e4fadf5a0388 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/common.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/common.ts @@ -32,6 +32,29 @@ export abstract class ActualResult { * Represents the "expected" results to compare */ export abstract class ExpectedResult { + /** + * The actual results must match exactly. Missing data + * will result in a failure + * + * @example + * // actual results + * const actual = { + * stringParam: 'hello', + * numberParam: 3, + * booleanParam: true, + * }; + * // pass + * ExpectedResult.exact({ + * stringParam: 'hello', + * numberParam: 3, + * booleanParam: true, + * }) + * + * // fail + * ExpectedResult.exact({ + * stringParam: 'hello', + * }); + */ public static exact(expected: any): ExpectedResult { return { result: JSON.stringify({ @@ -40,6 +63,22 @@ export abstract class ExpectedResult { }; } + /** + * The expected results must be a subset of the + * actual results. + * + * @example + * // actual results + * const actual = { + * stringParam: 'hello', + * numberParam: 3, + * booleanParam: true, + * }; + * // pass + * ExpectedResult.objectLike({ + * stringParam: 'hello', + * }); + */ public static objectLike(expected: { [key: string]: any }): ExpectedResult { return { result: JSON.stringify({ @@ -48,6 +87,27 @@ export abstract class ExpectedResult { }; } + /** + * The actual results must be a list and must contain + * an item with the expected results. + * + * @example + * // actual results + * const actual = [ + * { + * stringParam: 'hello', + * }, + * { + * stringParam: 'world', + * }, + * ]; + * // pass + * ExpectedResult.arrayWith([ + * { + * stringParam: 'hello', + * }, + * ]); + */ public static arrayWith(expected: any[]): ExpectedResult { return { result: JSON.stringify({ @@ -56,7 +116,15 @@ export abstract class ExpectedResult { }; } /** - * Expected result is a string + * Actual results is a string that matches + * the Expected result regex + * + * @example + * // actual results + * const actual = 'some string value'; + * + * // pass + * ExpectedResult.stringLikeRegexp('value'); */ public static stringLikeRegexp(expected: string): ExpectedResult { return { @@ -67,7 +135,7 @@ export abstract class ExpectedResult { } /** - * The expected results as a string + * The expected results encoded as a string */ public abstract result: string; } diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts b/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts index d66a76a5f2dd1..49ec225873fdd 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts @@ -7,9 +7,6 @@ import { AwsApiCall, LambdaInvokeFunction, LambdaInvokeFunctionProps } from './s const DEPLOY_ASSERT_SYMBOL = Symbol.for('@aws-cdk/integ-tests.DeployAssert'); -// keep this import separate from other imports to reduce chance for merge conflicts with v2-main -// eslint-disable-next-line no-duplicate-imports, import/order - // keep this import separate from other imports to reduce chance for merge conflicts with v2-main // eslint-disable-next-line no-duplicate-imports, import/order @@ -67,9 +64,9 @@ export class DeployAssert extends CoreConstruct { * const message = assert.awsApiCall('SQS', 'receiveMessage', { * QueueUrl: 'url', * }); - * message.assertObjectLike({ + * message.assert(ExpectedResult.objectLike({ * Messages: [{ Body: 'hello' }], - * }); + * })); */ public awsApiCall(service: string, api: string, parameters?: any): AwsApiCall { return new AwsApiCall(this, `AwsApiCall${service}${api}`, { @@ -88,15 +85,28 @@ export class DeployAssert extends CoreConstruct { * const invoke = assert.invokeFunction({ * functionName: 'my-function', * }); - * invoke.assertObjectLike({ + * invoke.assert(ExpectedResult.objectLike({ * Payload: '200', - * }); + * })); */ public invokeFunction(props: LambdaInvokeFunctionProps): LambdaInvokeFunction { const hash = md5hash(Stack.of(this).resolve(props)); return new LambdaInvokeFunction(this, `LambdaInvoke${hash}`, props); } + /** + * Assert that the ExpectedResult is equal + * to the ActualResult + * + * @example + * declare const deployAssert: DeployAssert; + * declare const apiCall: AwsApiCall; + * deployAssert.assert( + * 'invoke', + * ExpectedResult.objectLike({ Payload: 'OK' }), + * ActualResult.fromAwsApiCall(apiCall, 'Body'), + * ); + */ public assert(id: string, expected: ExpectedResult, actual: ActualResult): void { new EqualsAssertion(this, `EqualsAssertion${id}`, { expected, diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/index.ts b/packages/@aws-cdk/integ-tests/lib/assertions/index.ts index c430b38482222..3a9defd954be9 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/index.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/index.ts @@ -3,3 +3,4 @@ export * from './sdk'; export * from './deploy-assert'; export * from './providers'; export * from './common'; +export * from './match'; diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts b/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts index f942b46f0d50c..b176c13456f37 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/sdk.ts @@ -96,6 +96,17 @@ export class AwsApiCall extends CoreConstruct { return this.sdkCallResource.getAttString(`apiCallResponse.${attributeName}`); } + /** + * Assert that the ExpectedResult is equal + * to the result of the AwsApiCall + * + * @example + * declare const assert: DeployAssert; + * const invoke = new LambdaInvokeFunction(assert, 'Invoke', { + * functionName: 'my-func', + * }); + * invoke.assert(ExpectedResult.objectLike({ Payload: 'OK' })); + */ public assert(expected: ExpectedResult): void { new EqualsAssertion(this, `AssertEquals${this.name}`, { expected, @@ -103,51 +114,43 @@ export class AwsApiCall extends CoreConstruct { }); } + /** + * Assert that the ExpectedResult is equal + * to the result of the AwsApiCall at the given path. + * + * For example the SQS.receiveMessage api response would look + * like: + * + * If you wanted to assert the value of `Body` you could do + * + * @example + * const actual = { + * Messages: [{ + * MessageId: '', + * ReceiptHandle: '', + * MD5OfBody: '', + * Body: 'hello', + * Attributes: {}, + * MD5OfMessageAttributes: {}, + * MessageAttributes: {} + * }] + * }; + * + * + * declare const assert: DeployAssert; + * const message = new AwsApiCall(assert, 'ReceiveMessage', { + * service: 'SQS', + * api: 'receiveMessage' + * }); + * + * message.assertAtPath('Messages.0.Body', ExpectedResult.stringLikeRegexp('hello')); + */ public assertAtPath(path: string, expected: ExpectedResult): void { new EqualsAssertion(this, `AssertEquals${this.name}`, { expected, actual: ActualResult.fromAwsApiCall(this, path), }); } - - // /** - // * Asserts that the expected value is strictly equal to - // * the result of the AwsApiCall. Missing fields will result in - // * a failure. - // */ - // public assertObjectEqual(expected: { [key: string]: any }): void { - // new EqualsAssertion(this, `AssertEquals${this.name}`, { - // expected: ExpectedResult.fromObject(expected), - // actual: ActualResult.fromCustomResource(this.sdkCallResource, 'apiCallResponse'), - // }); - // } - - // /** - // * Asserts that the expected value is a subset of - // * the result of the AwsApiCall - // */ - // public assertObjectLike(expected: { [key: string]: any }): void { - // new EqualsAssertion(this, `EqualsAssertion${this.name}`, { - // actual: ActualResult.fromCustomResource(this.sdkCallResource, 'apiCallResponse'), - // expected: ExpectedResult.fromObject({ - // $ObjectLike: expected, - // }), - // assertionType: AssertionType.OBJECT_LIKE, - // }); - // } - - // /** - // * Asserts that the expected value is equal to the - // * results of the AwsApiCall. The result of the SdkQuery - // * must be a string value. - // */ - // public assertStringEqual(expected: string): void { - // new EqualsAssertion(this, `EqualsAssertion${this.name}`, { - // actual: ActualResult.fromCustomResource(this.sdkCallResource, 'apiCallResponse'), - // expected: ExpectedResult.fromString(expected), - // assertionType: AssertionType.EQUALS, - // }); - // } } /** diff --git a/packages/@aws-cdk/integ-tests/lib/test-case.ts b/packages/@aws-cdk/integ-tests/lib/test-case.ts index 0937fc6672930..de701bb63d24a 100644 --- a/packages/@aws-cdk/integ-tests/lib/test-case.ts +++ b/packages/@aws-cdk/integ-tests/lib/test-case.ts @@ -33,7 +33,7 @@ export class IntegTestCase extends CoreConstruct { */ public readonly assert: DeployAssert; - constructor(scope: Construct, private readonly id: string, private readonly props: IntegTestCaseProps) { + constructor(scope: Construct, id: string, private readonly props: IntegTestCaseProps) { super(scope, id); this.assert = new DeployAssert(this); @@ -46,7 +46,7 @@ export class IntegTestCase extends CoreConstruct { get manifest(): IntegManifest { return { version: Manifest.version(), - testCases: { [this.id]: this.toTestCase(this.props) }, + testCases: { [this.node.path]: this.toTestCase(this.props) }, }; } @@ -129,7 +129,7 @@ export class IntegTest extends CoreConstruct { constructor(scope: Construct, id: string, props: IntegTestProps) { super(scope, id); - const defaultTestCase = new IntegTestCase(this, 'DefaultTestCase', { + const defaultTestCase = new IntegTestCase(this, 'DefaultTest', { stacks: props.testCases.filter(stack => !IntegTestCaseStack.isIntegTestCaseStack(stack)), hooks: props.hooks, regions: props.regions, diff --git a/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture b/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture index 087886f752234..b9b4f3740b427 100644 --- a/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/integ-tests/rosetta/default.ts-fixture @@ -10,6 +10,8 @@ import { ExpectedResult, InvocationType, AssertionType, + LambdaInvokeFunction, + Match, } from '@aws-cdk/integ-tests'; import { App, diff --git a/packages/@aws-cdk/integ-tests/test/manifest-synthesizer.test.ts b/packages/@aws-cdk/integ-tests/test/manifest-synthesizer.test.ts index 3863da2fceea3..a8b564ad11910 100644 --- a/packages/@aws-cdk/integ-tests/test/manifest-synthesizer.test.ts +++ b/packages/@aws-cdk/integ-tests/test/manifest-synthesizer.test.ts @@ -43,11 +43,11 @@ describe(IntegManifestSynthesizer, () => { expect(write).toHaveBeenCalledWith({ version: Manifest.version(), testCases: { - case1: { + ['stack/case1']: { assertionStack: expect.stringMatching(/DeployAssert/), stacks: ['stack-under-test-1'], }, - case2: { + ['stack/case2']: { assertionStack: expect.stringMatching(/DeployAssert/), stacks: ['stack-under-test-2'], }, @@ -71,7 +71,7 @@ describe(IntegManifestSynthesizer, () => { expect(integManifest).toEqual({ version: Manifest.version(), testCases: { - DefaultTestCase: { + ['Integ/DefaultTest']: { assertionStack: expect.stringMatching(/DeployAssert/), stacks: ['stack'], }, @@ -98,11 +98,11 @@ describe(IntegManifestSynthesizer, () => { expect(integManifest).toEqual({ version: Manifest.version(), testCases: { - DefaultTestCase: { + ['Integ/DefaultTest']: { assertionStack: expect.stringMatching(/DeployAssert/), stacks: ['stack'], }, - CaseTestCase: { + ['Case/CaseTestCase']: { assertionStack: expect.stringMatching(/DeployAssert/), diffAssets: true, stacks: ['Case'], From 875a839d6fd9e2316888dd5cc6189945f5e421bc Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Wed, 4 May 2022 19:48:10 +0000 Subject: [PATCH 07/10] add option to report assertion failures to CloudFormation --- .../integ-tests/lib/assertions/assertions.ts | 12 ++++++++++++ .../providers/lambda-handler/assertion.ts | 3 +++ .../providers/lambda-handler/types.ts | 13 +++++++++++++ .../providers/lambda-handler/assertion.test.ts | 18 ++++++++++++++++++ 4 files changed, 46 insertions(+) diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/assertions.ts b/packages/@aws-cdk/integ-tests/lib/assertions/assertions.ts index 2b593d177e6a7..1d43336d9b4a8 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/assertions.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/assertions.ts @@ -21,6 +21,17 @@ export interface EqualsAssertionProps { * The expected result to assert */ readonly expected: ExpectedResult; + + /** + * Set this to true if a failed assertion should + * result in a CloudFormation deployment failure + * + * This is only necessary if assertions are being + * executed outside of `integ-runner`. + * + * @default false + */ + readonly reportFailure?: boolean; } /** @@ -40,6 +51,7 @@ export class EqualsAssertion extends CoreConstruct { const properties: AssertionRequest = { actual: props.actual.result, expected: props.expected.result, + reportFailure: props.reportFailure, }; const resource = new CustomResource(this, 'Default', { serviceToken: assertionProvider.serviceToken, diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts index 1d49341c2980b..54692d918a451 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts @@ -24,6 +24,9 @@ export class AssertionHandler extends CustomResourceHandler { }); describe('AssertionHandler', () => { + test('report failure', async () => { + // GIVEN + const handler = assertionHandler() as any; + const request: AssertionRequest = { + actual: 'this is the actual results', + expected: ExpectedResult.stringLikeRegexp('abcd').result, + reportFailure: true, + }; + + // THEN + let failed: Error = new Error(); + try { + await handler.processEvent(request); + } catch (e) { + failed = e; + } + expect(failed.message).toMatch(/String 'this is the actual results' did not match pattern 'abcd' (using stringLikeRegexp matcher)*/); + }); describe('stringLike', () => { test('pass', async () => { // GIVEN From a2e6f8d5aa08af0c50a4490f87faf3a4897a8e90 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Thu, 5 May 2022 11:46:33 +0000 Subject: [PATCH 08/10] adding missing docstring --- .../lib/assertions/providers/lambda-handler/types.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/types.ts b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/types.ts index a8296b1e05c9f..d0a631c099759 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/types.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/types.ts @@ -114,6 +114,11 @@ export interface AssertionResult { */ readonly data: string; + /** + * Whether or not the assertion failed + * + * @default false + */ readonly failed?: boolean; } From 86da9c5fef5ba7ca07d37845bc5314e439a3d86f Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Mon, 16 May 2022 14:08:29 +0000 Subject: [PATCH 09/10] updates based on pr comments --- .../integ-tests/lib/assertions/assertions.ts | 4 +-- .../providers/lambda-handler/assertion.ts | 36 ++++++------------- .../providers/lambda-handler/types.ts | 2 +- .../lambda-handler/assertion.test.ts | 2 +- 4 files changed, 14 insertions(+), 30 deletions(-) diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/assertions.ts b/packages/@aws-cdk/integ-tests/lib/assertions/assertions.ts index 1d43336d9b4a8..c7f20e005e526 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/assertions.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/assertions.ts @@ -31,7 +31,7 @@ export interface EqualsAssertionProps { * * @default false */ - readonly reportFailure?: boolean; + readonly failDeployment?: boolean; } /** @@ -51,7 +51,7 @@ export class EqualsAssertion extends CoreConstruct { const properties: AssertionRequest = { actual: props.actual.result, expected: props.expected.result, - reportFailure: props.reportFailure, + failDeployment: props.failDeployment, }; const resource = new CustomResource(this, 'Default', { serviceToken: assertionProvider.serviceToken, diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts index 54692d918a451..fdb7bb04b7bdd 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/providers/lambda-handler/assertion.ts @@ -24,7 +24,7 @@ export class AssertionHandler extends CustomResourceHandler { const request: AssertionRequest = { actual: 'this is the actual results', expected: ExpectedResult.stringLikeRegexp('abcd').result, - reportFailure: true, + failDeployment: true, }; // THEN From 619b2f9ce29c7c6f7e80d5370fe56aabdfd5fb08 Mon Sep 17 00:00:00 2001 From: corymhall <43035978+corymhall@users.noreply.github.com> Date: Mon, 16 May 2022 15:14:44 +0000 Subject: [PATCH 10/10] adding comment about scope swapparoo --- .../integ-tests/lib/assertions/deploy-assert.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts b/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts index 49ec225873fdd..24bbfd6789fbf 100644 --- a/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts +++ b/packages/@aws-cdk/integ-tests/lib/assertions/deploy-assert.ts @@ -43,6 +43,18 @@ export class DeployAssert extends CoreConstruct { } constructor(scope: Construct) { + /** + * Normally we would not want to do a scope swapparoo like this + * but in this case this it allows us to provide a better experience + * for the user. This allows DeployAssert to be created _not_ in the + * scope of a Stack. DeployAssert is treated like a Stack, but doesn't + * exose any of the stack functionality (the methods that the user sees + * are just DeployAssert methods and not any Stack methods). So you can do + * something like this, which you would not normally be allowed to do + * + * const deployAssert = new DeployAssert(app); + * new AwsApiCall(deployAssert, 'AwsApiCall', {...}); + */ scope = new Stack(scope, 'DeployAssert'); super(scope, 'Default');