Skip to content

(aws-cdk-lib/assertions): Simplify assertions on stack tags #27620

@jamestelfer

Description

@jamestelfer

Describe the feature

Tags associated with a Cloudformation stack are recorded in the cloud assembly manifest. The existing Template and Annotations classes in aws-cdk-lib/assertions allow for simplified testing of some parts of the cloud assembly, but not the tags member.

It is possible to get the data from the manifest by interrogating the cloud assembly, but this is far less straightforward than the existing mechanisms.

Use Case

We have constructs libraries that ensure our stacks are tagged appropriately for a project. Currently tests for this behaviour are possible but not straightforward.

Proposed Solution

I suggest additions the API such that it would be possible to write assertions similar to:

// Tags aren't technically part of the template, though this kinda makes sense
Template.fromStack(foo).hasTags({
  "tag-name":"tag-value",
  "other-tag": Matcher.absent()
})

// OR - this is targeted carefully at this case
Tags.fromStack(foo).hasTags(Matcher.anyValue())

// OR - more generally, could be use for other manifest properties but I don't know what they would be.
StackProperties.fromStack(foo).hasTags(Matcher.anyValue())

Other Information

It is possible to write these tests outside of the CDK assertions library by interrogating the Cloud Assembly API. 4

Something like the following works:

test("foo", () => {
  const app = new TestApp()
  const stack = new TestStack(app, "name", {
    tags: { "tag-name": "tag-value" },
  })

  const stage = Stage.of(stack)
  if (!Stage.isStage(stage)) {
    throw new Error("unexpected: all stacks must be part of a Stage or an App")
  }

  const assembly = stage.synth()
  const tags = assembly.getStackArtifact(stack.artifactId).tags

  expect(tags).toMatchObject({
    "tag-name": "tag-value",
  })
})

There are some special cases that occur when looking up details in the assembly in both Template and Annotations classes. Respectively, handling nested stacks and forcing synthesis. I'm not sure which of these special cases should be handled in the tag case.

Template handling of nested stacks:

if (stack.nestedStackParent) {
// if this is a nested stack (it has a parent), then just read the template as a string
return JSON.parse(fs.readFileSync(path.join(assembly.directory, stack.templateFile)).toString('utf-8'));
}

Annotations forcing synthesis:

// to support incremental assertions (i.e. "expect(stack).toNotContainSomething(); doSomething(); expect(stack).toContainSomthing()")
const force = true;
const assembly = root.synth({ force });

I've started on a branch to feel out what an implementation would look like. If the Tags API is selected, the PR would be quite small by the looks.

Acknowledgements

  • I may be able to implement this feature request
  • This feature might incur a breaking change

CDK version used

2.102.0

Environment details (OS name and version, etc.)

N/A

Metadata

Metadata

Assignees

No one assigned

    Labels

    @aws-cdk/assertionsRelated to the @aws-cdk/assertv2 packageeffort/mediumMedium work item – several days of effortfeature-requestA feature should be added or improved.p1

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions