Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(assertions): Capture captures value from last resource with matching type, not matched props #17009

Closed
poWer4aiX opened this issue Oct 15, 2021 · 5 comments · Fixed by #17713
Labels
@aws-cdk/assertions Related to the @aws-cdk/assertv2 package bug This issue is a bug. effort/medium Medium work item – several days of effort p1

Comments

@poWer4aiX
Copy link

What is the problem?

I used Template.findResources() to filter for one resource in a stack, by type and value of the Name tag. For one property I used a Capture(). My expectation was that the captured value will be the one of the resource found, which was not the case. Instead, it was the value from the resource in the stack matching just the given type.

Reproduction Steps

test('capture', () => {
	const capture = new Capture();
	const resources = {
		Resources: {
			res1: { Type: 'res', Name: 'a', Prop: 1 },
			res2: { Type: 'res', Name: 'b', Prop: 2 }
		}
	}
	const template = Template.fromJSON(resources)
	const res = template.findResources('res', {
		Name: 'a', Prop: capture
	})
	// filtered resource is the correct one.
	console.log(JSON.stringify(res, null, 4))
	// fails, as the value (2) of the last 'Prop' from all resources is
	// captured, not the one of the last matching resource (1).
	expect(capture.asNumber()).toEqual(1);
})

What did you expect to happen?

The captured value should be the value from the last resource being filtered (i.e. by type and optional attributes). In the example code given above this should be 1 instead of 2.

What actually happened?

The captured value is the one of the attribute from the last resource of the resource type, independent if this one matched or not.

CDK CLI Version

1.128.0

Framework Version

1.128.0

Node.js Version

14.17.4

OS

Ubuntu 20.04, running in an WSL on Windows 10

Language

Typescript

Language Version

3.9.7

Other information

No response

@poWer4aiX poWer4aiX added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Oct 15, 2021
@github-actions github-actions bot added the package/tools Related to AWS CDK Tools or CLI label Oct 15, 2021
@poWer4aiX poWer4aiX changed the title (@aws-cdk/assertions): Capture captures value from last resource with matching type, not matched props (assertions): Capture captures value from last resource with matching type, not matched props Oct 15, 2021
@rix0rrr rix0rrr assigned nija-at and unassigned rix0rrr Oct 20, 2021
@ryparker ryparker added effort/small Small work item – less than a day of effort and removed needs-triage This issue or PR still needs to be triaged. labels Oct 22, 2021
@nija-at
Copy link
Contributor

nija-at commented Oct 25, 2021

@poWer4aiX -

This is interesting. The Capture class was never intended to be used as part of findXxx() APIs, only with hasXxx() APIs.
But at the same time, it was never explicitly forbidden.

Can you switch to using the hasXxx() APIs and see if that fixes your problem?

@nija-at nija-at added @aws-cdk/assertions Related to the @aws-cdk/assertv2 package response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. and removed p1 package/tools Related to AWS CDK Tools or CLI effort/small Small work item – less than a day of effort labels Oct 25, 2021
@poWer4aiX
Copy link
Author

@nija-at As the way how the template is being processed does not differ between the findXxx() and hasXxx() APIs I used the Capture...

The same issue exists as well when using hasXxx() instead of findXxx() (same processing). I have already extended the tests in tests/template.test.ts not only for findXxx(), but also for hasXxx() (around line 271):

    test('captures values from matched resources', () => {
      const stack = new Stack();
      new CfnResource(stack, 'res1', { type: 'res', properties: { Name: 'foo', fred: 'waldo' } });
      new CfnResource(stack, 'res2', { type: 'res', properties: { Name: 'bar', fred: 'fred' } });

      const inspect = Template.fromStack(stack);
      const capture = new Capture();
      inspect.hasResource('res', { Properties: { Name: 'foo', fred: capture } });
      expect(capture.asString()).toEqual('waldo');
    });

You can simply disable my fix in lib/private/section.ts (around line 29), by adding a false &&:

if (false && Object.keys(section).length > Object.keys(matching).length) {

Running a test with yarn test will fail now, as the capture is being applied against the non matching second resource as well, resulting in capturing the value from the non matching resource:

$ yarn test
yarn run v1.22.15
$ cdk-test
PASS test/capture.test.js (5.494 s)
PASS test/match.test.js (5.728 s)
PASS test/private/section.test.js
FAIL test/template.test.js (6.958 s)
  ● Template › hasResource › captures values from matched resources

    expect(received).toEqual(expected) // deep equality

    Expected: "waldo"
    Received: "fred"

      277 |       const capture = new Capture();
      278 |       inspect.hasResource('res', { Properties: { Name: 'foo', fred: capture } });
    > 279 |       expect(capture.asString()).toEqual('waldo');
          |                                  ^
      280 |     });
      281 |   });
      282 |

      at Object.<anonymous> (test/template.test.ts:279:34)

Well my fix is quiet simple as it tests the matcher again, but on the reduced / filtered / matched subset of resources.

@github-actions github-actions bot removed the response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. label Oct 26, 2021
@nija-at
Copy link
Contributor

nija-at commented Oct 28, 2021

Got it. Thanks for the update @poWer4aiX

@nija-at nija-at added effort/small Small work item – less than a day of effort p1 effort/medium Medium work item – several days of effort and removed effort/small Small work item – less than a day of effort labels Oct 28, 2021
@nija-at
Copy link
Contributor

nija-at commented Nov 5, 2021

Originally posted by @nija-at in #17012 (review)

First, we need to decide what is the correct behaviour if Capture is used when there are multiple matching entries. There are two options -

(1) return all of them in a series such as

captured.asString(); // "Foo"
captured.next().asString(); // "Bar"
...

or
(2) throw an error in such a case.

In either case, the solution should be in the Capture class to hold state of all values that has flown through it, and finally decide how to handle them.

@nija-at nija-at removed their assignment Dec 2, 2021
@mergify mergify bot closed this as completed in #17713 Dec 2, 2021
mergify bot pushed a commit that referenced this issue Dec 2, 2021
There are three major changes around the capture feature of assertions.

Firstly, when there are multiple targets (say, Resource in the
CloudFormation template) that matches the given condition, any
`Capture` defined in the condition will contain only the last matched
resource.
Convert the `Capture` class into an iterable so all matching values can
be retrieved.

Secondly, add support to allow sub-patterns to be specified to the
`Capture` class. This allows further conditions be specified, via
Matchers or literals, when a value is to be captured.

Finally, this fixes a bug with the current implementation where
`Capture` contains the results of the last matched section, irrespective
of whether that section matched with the rest of the matcher or not.

fixes #17009

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
@github-actions
Copy link

github-actions bot commented Dec 2, 2021

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

TikiTDO pushed a commit to TikiTDO/aws-cdk that referenced this issue Feb 21, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
There are three major changes around the capture feature of assertions.

Firstly, when there are multiple targets (say, Resource in the
CloudFormation template) that matches the given condition, any
`Capture` defined in the condition will contain only the last matched
resource.
Convert the `Capture` class into an iterable so all matching values can
be retrieved.

Secondly, add support to allow sub-patterns to be specified to the
`Capture` class. This allows further conditions be specified, via
Matchers or literals, when a value is to be captured.

Finally, this fixes a bug with the current implementation where
`Capture` contains the results of the last matched section, irrespective
of whether that section matched with the rest of the matcher or not.

fixes aws#17009

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/assertions Related to the @aws-cdk/assertv2 package bug This issue is a bug. effort/medium Medium work item – several days of effort p1
Projects
None yet
5 participants