aws / aws-cdk

The AWS Cloud Development Kit is a framework for defining cloud infrastructure in code
https://aws.amazon.com/cdk
Apache License 2.0
11.59k stars 3.89k forks source link

AWS CDK : Unit tests for Java for Resources with HostedZone.fromHostedZoneAttributes #18071

Closed aritranagnordcloud closed 2 years ago

aritranagnordcloud commented 2 years ago

General Issue

CDK Unit tests failing for Lookups and existing Resources

The Question

Hi CDK team,

We have been using the Java CDK toolkit to create the solution construct for our application. We have an CDK construct which gets created using existing services like Route53 , VPC. For the normal cdk synth command without any test cases it works absolutely fine. While running the test cases via the AWS provided framework awscdk.assertions.Template. We could see the following error and results in failing the tests.

**software.amazon.jsii.JsiiException: Got 'undefined' for non-optional instance of {"type":{"fqn":"@aws-cdk/aws-route53.AliasRecordTargetConfig"}}**

Actual code used :

RecordSet recordSet = new RecordSet(this, "PrivateRecord",
                RecordSetProps.builder().recordName(id.concat("-app").concat(".").concat(stackProps.getDnsDomain()))
                        .recordType(RecordType.A)
                        .target(RecordTarget.fromAlias(new IAliasRecordTarget() {
                            @Override
                            public @NotNull AliasRecordTargetConfig bind(@NotNull IRecordSet record) {
                                return AliasRecordTargetConfig.builder().dnsName(stackProps.getCommonAlbDnsName())
                                        .hostedZoneId(stackProps.getCommonAlbHostedZoneId()).build();
                            }
                            @Override
                            public @NotNull AliasRecordTargetConfig bind(@NotNull IRecordSet record, IHostedZone zone) {
                                return null;
                            }
                        }))
                        .zone(HostedZone.fromHostedZoneAttributes(this, "ExistingHostedZone",
                                HostedZoneAttributes.builder()
                                        .hostedZoneId(stackProps.getDistributionZoneId())
                                        .zoneName(stackProps.getDnsDomain()).build()))
                        .build())

Please help us in finding the solution to create the unit tests. Is the reason maybe due to disabling of lookups in the pipelines

Thanks, Aritra

CDK CLI Version

1.134.0

Framework Version

No response

Node.js Version

No response

OS

Mac

Language

Java

Language Version

java 11

Other information

No response

aritranagnordcloud commented 2 years ago

Do we have any AWS CDK patterns for unit tests. Can we use the native mock libraries for the AWS Services Construct?

peterwoodworth commented 2 years ago

Hey @aritranagnordcloud,

This is probably because your tests don't have environment variables properly configured. Your tests don't read from your cdk.json file; you have to set the context with something like the setContext() method.

Let me know if that's not the cause here - and if it's not could you share your test code?

aritranagnordcloud commented 2 years ago

Here is the test code:

stackProps are the other values which are required to create the different construct in the cdk synth.

`

CdkStack stack = new CdkStack(app, "test", new StackProps.Builder() .env(CdkApp.makeEnv("accountno", "region")) .build(), stackProps); Template template = Template.fromStack(stack); template.hasResourceProperties("AWS::Logs::LogGroup", new HashMap<String, Number>() {{ put("RetentionInDays", 7); }}); `

I am not sure if cdk.json is being used in the test folder or how do i use that in the command. Also, the command to build the whole project is ./gradlew clean build test

aritranagnordcloud commented 2 years ago

any leads about the CDK Unit test package structure would be really helpful. Since i could not find any way to run the unit test

peterwoodworth commented 2 years ago

I am not sure if cdk.json is being used in the test folder or how do i use that in the command

For the CDK's tests that require environment variables to be set, we use the setContext() method I linked above to set the appropriate context to the constructs that need it

Since i could not find any way to run the unit test

Here's our documentation page on testing CDK constructs https://docs.aws.amazon.com/cdk/v2/guide/testing.html

According to this page, this is the command you should be using to run tests:

mvn compile && mvn test

aritranagnordcloud commented 2 years ago

This issue seems to be very intermittent with the setup. Here is the setup for the code which is breaking:

RecordSet recordSet = new RecordSet(this, "PrivateRecord",
    RecordSetProps.builder()
    .recordName(id.concat("-app").concat(".")
        .concat(stackProps.getDnsDomain()))
    .recordType(RecordType.A)
    .target(RecordTarget.fromAlias(new IAliasRecordTarget() {
        @Override
        public @NotNull AliasRecordTargetConfig bind(
            @NotNull IRecordSet record) {
            return AliasRecordTargetConfig.builder()
                .dnsName(stackProps
                    .getCommonAlbDnsName())
                .hostedZoneId(stackProps
                    .getCommonAlbHostedZoneId())
                .build();
        }
        @Override
        public @NotNull AliasRecordTargetConfig bind(
            @NotNull IRecordSet record, IHostedZone zone) {
            return null;
        }
    }))
    .zone(HostedZone.fromHostedZoneAttributes(this, "ExistingHostedZone",
        HostedZoneAttributes.builder()
        .hostedZoneId(stackProps
            .getDistributionZoneId())
        .zoneName(stackProps.getDnsDomain())
        .build()))
    .build());

I checked the incoming props value and its working properly.

The error which is coming :

software.amazon.jsii.JsiiException: Got 'undefined' for non-optional instance of {"type":{"fqn":"@aws-cdk/aws-route53.AliasRecordTargetConfig"}}
    Error: Got 'undefined' for non-optional instance of {"type":{"fqn":"@aws-cdk/aws-route53.AliasRecordTargetConfig"}}
        at nullAndOk (/tmp/jsii-java-runtime6420813574840657886/lib/program.js:9596:23)
        at Object.deserialize (/tmp/jsii-java-runtime6420813574840657886/lib/program.js:9315:25)
        at Kernel._toSandbox (/tmp/jsii-java-runtime6420813574840657886/lib/program.js:8783:69)
        at Object.value (/tmp/jsii-java-runtime6420813574840657886/lib/program.js:8611:41)
        at new RecordSet (/tmp/jsii-kernel-Phx3Hv/node_modules/@aws-cdk/aws-route53/lib/record-set.js:100:79)
        at /tmp/jsii-java-runtime6420813574840657886/lib/program.js:8432:58
        at Kernel._wrapSandboxCode (/tmp/jsii-java-runtime6420813574840657886/lib/program.js:8860:24)
        at Kernel._create (/tmp/jsii-java-runtime6420813574840657886/lib/program.js:8432:34)
        at Kernel.create (/tmp/jsii-java-runtime6420813574840657886/lib/program.js:8173:29)
        at KernelHost.processRequest (/tmp/jsii-java-runtime6420813574840657886/lib/program.js:9757:36)
        at app//software.amazon.jsii.JsiiRuntime.processErrorResponse(JsiiRuntime.java:124)
        at app//software.amazon.jsii.JsiiRuntime.requestResponse(JsiiRuntime.java:95)
        at app//software.amazon.jsii.JsiiRuntime.processCallbackResponse(JsiiRuntime.java:165)
        at app//software.amazon.jsii.JsiiRuntime.requestResponse(JsiiRuntime.java:100)
        at app//software.amazon.jsii.JsiiClient.createObject(JsiiClient.java:89)
        at app//software.amazon.jsii.JsiiEngine.createNewObject(JsiiEngine.java:603)
        at app//software.amazon.awscdk.services.route53.RecordSet.<init>(RecordSet.java:47)
aritranagnordcloud commented 2 years ago

Coming to the build and library plugin, we are. using gradle instead of maven

aritranagnordcloud commented 2 years ago

Do you have any examples of how to set the context forcibly on the app on the text folder. For example , when we invoke the same code from the src/main method , it works absolutely fine.

aritranagnordcloud commented 2 years ago

Any leads on this would be really helpful. I tried creating the unit tests in the following way:

App app = new App();
        IBackendStackProps stackProps = new IBackendStackProps();
        stackProps.setDnsDomain("test.domainname.com");
       stackProps.setCommonAlbHostedZoneId("12345678");
        stackProps.setCommonAlbDnsName("helloworld.region.elb.amazonaws.com");
stackProps.setDistributionZoneId("123456333455");
CdkStack stack = new CdkStack(app, "test", new StackProps.Builder()
        .env(CdkApp.makeEnv("accountid", "region"))
        .build(), stackProps);
        Template template = Template.fromStack(stack);
 template.hasResourceProperties("AWS::Route53::RecordSet", new HashMap<String, String>() {{
          put("Type", A);
        }});

When running mvn compile && mvn test, its failing and generating the above error.

peterwoodworth commented 2 years ago

Do you have any examples of how to set the context forcibly on the app on the text folder.

Have you tried the SetContext() method I've described above?

Here's an example of the CDK using this method to set the context in our tests https://github.com/aws/aws-cdk/blob/3721358fa1501e42b3514b8a8f15f05c9615f149/packages/%40aws-cdk/core/test/construct.test.ts#L163-L176

To do this with a Stack, you will also use Stack.node.setContext().